bittensor-cli 9.0.0rc2__py3-none-any.whl → 9.0.0rc3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bittensor_cli/__init__.py +1 -1
- bittensor_cli/cli.py +440 -157
- bittensor_cli/src/__init__.py +1 -0
- bittensor_cli/src/bittensor/subtensor_interface.py +1 -1
- bittensor_cli/src/bittensor/utils.py +14 -0
- bittensor_cli/src/commands/stake/add.py +625 -0
- bittensor_cli/src/commands/stake/list.py +687 -0
- bittensor_cli/src/commands/stake/move.py +1 -1
- bittensor_cli/src/commands/stake/remove.py +1146 -0
- bittensor_cli/src/commands/wallets.py +24 -32
- {bittensor_cli-9.0.0rc2.dist-info → bittensor_cli-9.0.0rc3.dist-info}/METADATA +1 -1
- {bittensor_cli-9.0.0rc2.dist-info → bittensor_cli-9.0.0rc3.dist-info}/RECORD +15 -13
- bittensor_cli/src/commands/stake/stake.py +0 -1821
- {bittensor_cli-9.0.0rc2.dist-info → bittensor_cli-9.0.0rc3.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.0.0rc2.dist-info → bittensor_cli-9.0.0rc3.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.0.0rc2.dist-info → bittensor_cli-9.0.0rc3.dist-info}/top_level.txt +0 -0
bittensor_cli/cli.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
import asyncio
|
3
3
|
import curses
|
4
|
+
import importlib
|
4
5
|
import os.path
|
5
6
|
import re
|
6
7
|
import ssl
|
@@ -32,7 +33,13 @@ from async_substrate_interface.errors import SubstrateRequestException
|
|
32
33
|
from bittensor_cli.src.commands import sudo, wallets
|
33
34
|
from bittensor_cli.src.commands import weights as weights_cmds
|
34
35
|
from bittensor_cli.src.commands.subnets import price, subnets
|
35
|
-
from bittensor_cli.src.commands.stake import
|
36
|
+
from bittensor_cli.src.commands.stake import (
|
37
|
+
children_hotkeys,
|
38
|
+
list as list_stake,
|
39
|
+
move as move_stake,
|
40
|
+
add as add_stake,
|
41
|
+
remove as remove_stake,
|
42
|
+
)
|
36
43
|
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
37
44
|
from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
|
38
45
|
from bittensor_cli.src.bittensor.utils import (
|
@@ -50,9 +57,9 @@ from bittensor_cli.src.bittensor.utils import (
|
|
50
57
|
prompt_for_subnet_identity,
|
51
58
|
print_linux_dependency_message,
|
52
59
|
is_linux,
|
60
|
+
validate_rate_tolerance,
|
53
61
|
)
|
54
62
|
from typing_extensions import Annotated
|
55
|
-
from textwrap import dedent
|
56
63
|
from websockets import ConnectionClosed, InvalidHandshake
|
57
64
|
from yaml import safe_dump, safe_load
|
58
65
|
|
@@ -65,7 +72,7 @@ except ImportError:
|
|
65
72
|
pass
|
66
73
|
|
67
74
|
|
68
|
-
__version__ = "9.0.
|
75
|
+
__version__ = "9.0.0rc3"
|
69
76
|
|
70
77
|
|
71
78
|
_core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
|
@@ -169,13 +176,13 @@ class Options:
|
|
169
176
|
)
|
170
177
|
netuid = typer.Option(
|
171
178
|
None,
|
172
|
-
help="The netuid of the subnet in the
|
179
|
+
help="The netuid of the subnet in the network, (e.g. 1).",
|
173
180
|
prompt=True,
|
174
181
|
callback=validate_netuid,
|
175
182
|
)
|
176
183
|
netuid_not_req = typer.Option(
|
177
184
|
None,
|
178
|
-
help="The netuid of the subnet in the
|
185
|
+
help="The netuid of the subnet in the network, (e.g. 1).",
|
179
186
|
prompt=False,
|
180
187
|
)
|
181
188
|
all_netuids = typer.Option(
|
@@ -238,6 +245,28 @@ class Options:
|
|
238
245
|
help="Create wallet from uri (e.g. 'Alice', 'Bob', 'Charlie', 'Dave', 'Eve')",
|
239
246
|
callback=validate_uri,
|
240
247
|
)
|
248
|
+
rate_tolerance = typer.Option(
|
249
|
+
None,
|
250
|
+
"--slippage",
|
251
|
+
"--slippage-tolerance",
|
252
|
+
"--tolerance",
|
253
|
+
help="Set the rate tolerance percentage for transactions (default: 0.05%).",
|
254
|
+
callback=validate_rate_tolerance,
|
255
|
+
)
|
256
|
+
safe_staking = typer.Option(
|
257
|
+
None,
|
258
|
+
"--safe-staking/--no-safe-staking",
|
259
|
+
"--safe/--unsafe",
|
260
|
+
help="Enable or disable safe staking mode (default: enabled).",
|
261
|
+
)
|
262
|
+
allow_partial_stake = typer.Option(
|
263
|
+
None,
|
264
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
265
|
+
"--partial/--no-partial",
|
266
|
+
"--allow/--not-allow",
|
267
|
+
"--allow-partial/--not-partial",
|
268
|
+
help="Enable or disable partial stake mode (default: disabled).",
|
269
|
+
)
|
241
270
|
|
242
271
|
|
243
272
|
def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
|
@@ -504,6 +533,7 @@ class CLIManager:
|
|
504
533
|
subnets_app: typer.Typer
|
505
534
|
weights_app: typer.Typer
|
506
535
|
utils_app = typer.Typer(epilog=_epilog)
|
536
|
+
asyncio_runner = asyncio
|
507
537
|
|
508
538
|
def __init__(self):
|
509
539
|
self.config = {
|
@@ -512,25 +542,29 @@ class CLIManager:
|
|
512
542
|
"wallet_hotkey": None,
|
513
543
|
"network": None,
|
514
544
|
"use_cache": True,
|
515
|
-
"
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
545
|
+
"rate_tolerance": None,
|
546
|
+
"safe_staking": True,
|
547
|
+
"allow_partial_stake": False,
|
548
|
+
# Commenting this out as this needs to get updated
|
549
|
+
# "metagraph_cols": {
|
550
|
+
# "UID": True,
|
551
|
+
# "GLOBAL_STAKE": True,
|
552
|
+
# "LOCAL_STAKE": True,
|
553
|
+
# "STAKE_WEIGHT": True,
|
554
|
+
# "RANK": True,
|
555
|
+
# "TRUST": True,
|
556
|
+
# "CONSENSUS": True,
|
557
|
+
# "INCENTIVE": True,
|
558
|
+
# "DIVIDENDS": True,
|
559
|
+
# "EMISSION": True,
|
560
|
+
# "VTRUST": True,
|
561
|
+
# "VAL": True,
|
562
|
+
# "UPDATED": True,
|
563
|
+
# "ACTIVE": True,
|
564
|
+
# "AXON": True,
|
565
|
+
# "HOTKEY": True,
|
566
|
+
# "COLDKEY": True,
|
567
|
+
# },
|
534
568
|
}
|
535
569
|
self.subtensor = None
|
536
570
|
self.config_base_path = os.path.expanduser(defaults.config.base_path)
|
@@ -629,7 +663,7 @@ class CLIManager:
|
|
629
663
|
self.config_app.command("set")(self.set_config)
|
630
664
|
self.config_app.command("get")(self.get_config)
|
631
665
|
self.config_app.command("clear")(self.del_config)
|
632
|
-
self.config_app.command("metagraph")(self.metagraph_config)
|
666
|
+
self.config_app.command("metagraph", hidden=True)(self.metagraph_config)
|
633
667
|
|
634
668
|
# wallet commands
|
635
669
|
self.wallet_app.command(
|
@@ -923,9 +957,12 @@ class CLIManager:
|
|
923
957
|
ConnectionClosed,
|
924
958
|
SubstrateRequestException,
|
925
959
|
KeyboardInterrupt,
|
960
|
+
RuntimeError,
|
926
961
|
) as e:
|
927
962
|
if isinstance(e, SubstrateRequestException):
|
928
963
|
err_console.print(str(e))
|
964
|
+
elif isinstance(e, RuntimeError):
|
965
|
+
pass # Temporarily to handle loop bound issues
|
929
966
|
verbose_console.print(traceback.format_exc())
|
930
967
|
except Exception as e:
|
931
968
|
err_console.print(f"An unknown error has occurred: {e}")
|
@@ -937,15 +974,12 @@ class CLIManager:
|
|
937
974
|
try:
|
938
975
|
raise typer.Exit()
|
939
976
|
except Exception as e: # ensures we always exit cleanly
|
940
|
-
if not isinstance(
|
977
|
+
if not isinstance(
|
978
|
+
e, (typer.Exit, RuntimeError)
|
979
|
+
): # temporarily to handle multiple run commands in one session
|
941
980
|
err_console.print(f"An unknown error has occurred: {e}")
|
942
981
|
|
943
|
-
|
944
|
-
# For Python 3.9 or lower
|
945
|
-
return asyncio.get_event_loop().run_until_complete(_run())
|
946
|
-
else:
|
947
|
-
# For Python 3.10 or higher
|
948
|
-
return asyncio.run(_run())
|
982
|
+
return self.asyncio_runner(_run())
|
949
983
|
|
950
984
|
def main_callback(
|
951
985
|
self,
|
@@ -996,6 +1030,20 @@ class CLIManager:
|
|
996
1030
|
if k in self.config.keys():
|
997
1031
|
self.config[k] = v
|
998
1032
|
|
1033
|
+
if sys.version_info < (3, 10):
|
1034
|
+
# For Python 3.9 or lower
|
1035
|
+
self.asyncio_runner = asyncio.get_event_loop().run_until_complete
|
1036
|
+
else:
|
1037
|
+
try:
|
1038
|
+
uvloop = importlib.import_module("uvloop")
|
1039
|
+
if sys.version_info >= (3, 11):
|
1040
|
+
self.asyncio_runner = uvloop.run
|
1041
|
+
else:
|
1042
|
+
uvloop.install()
|
1043
|
+
self.asyncio_runner = asyncio.run
|
1044
|
+
except ModuleNotFoundError:
|
1045
|
+
self.asyncio_runner = asyncio.run
|
1046
|
+
|
999
1047
|
def verbosity_handler(self, quiet: bool, verbose: bool):
|
1000
1048
|
if quiet and verbose:
|
1001
1049
|
err_console.print("Cannot specify both `--quiet` and `--verbose`")
|
@@ -1052,9 +1100,44 @@ class CLIManager:
|
|
1052
1100
|
help="Disable caching of some commands. This will disable the `--reuse-last` and `--html` flags on "
|
1053
1101
|
"commands such as `subnets metagraph`, `stake show` and `subnets list`.",
|
1054
1102
|
),
|
1103
|
+
rate_tolerance: Optional[float] = typer.Option(
|
1104
|
+
None,
|
1105
|
+
"--slippage",
|
1106
|
+
"--slippage-tolerance",
|
1107
|
+
"--tolerance",
|
1108
|
+
help="Set the rate tolerance percentage for transactions (e.g. 0.1 for 0.1%).",
|
1109
|
+
),
|
1110
|
+
safe_staking: Optional[bool] = typer.Option(
|
1111
|
+
None,
|
1112
|
+
"--safe-staking/--no-safe-staking",
|
1113
|
+
"--safe/--unsafe",
|
1114
|
+
help="Enable or disable safe staking mode.",
|
1115
|
+
),
|
1116
|
+
allow_partial_stake: Optional[bool] = typer.Option(
|
1117
|
+
None,
|
1118
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
1119
|
+
"--partial/--no-partial",
|
1120
|
+
"--allow/--not-allow",
|
1121
|
+
),
|
1055
1122
|
):
|
1056
1123
|
"""
|
1057
|
-
Sets
|
1124
|
+
Sets or updates configuration values in the BTCLI config file.
|
1125
|
+
|
1126
|
+
This command allows you to set default values that will be used across all BTCLI commands.
|
1127
|
+
|
1128
|
+
USAGE
|
1129
|
+
Interactive mode:
|
1130
|
+
[green]$[/green] btcli config set
|
1131
|
+
|
1132
|
+
Set specific values:
|
1133
|
+
[green]$[/green] btcli config set --wallet-name default --network finney
|
1134
|
+
[green]$[/green] btcli config set --safe-staking --rate-tolerance 0.1
|
1135
|
+
|
1136
|
+
[bold]NOTE[/bold]:
|
1137
|
+
- Network values can be network names (e.g., 'finney', 'test') or websocket URLs
|
1138
|
+
- Rate tolerance is specified as a decimal (e.g., 0.05 for 0.05%)
|
1139
|
+
- Changes are saved to ~/.bittensor/btcli.yaml
|
1140
|
+
- Use '[green]$[/green] btcli config get' to view current settings
|
1058
1141
|
"""
|
1059
1142
|
args = {
|
1060
1143
|
"wallet_name": wallet_name,
|
@@ -1062,8 +1145,11 @@ class CLIManager:
|
|
1062
1145
|
"wallet_hotkey": wallet_hotkey,
|
1063
1146
|
"network": network,
|
1064
1147
|
"use_cache": use_cache,
|
1148
|
+
"rate_tolerance": rate_tolerance,
|
1149
|
+
"safe_staking": safe_staking,
|
1150
|
+
"allow_partial_stake": allow_partial_stake,
|
1065
1151
|
}
|
1066
|
-
bools = ["use_cache"]
|
1152
|
+
bools = ["use_cache", "safe_staking", "allow_partial_stake"]
|
1067
1153
|
if all(v is None for v in args.values()):
|
1068
1154
|
# Print existing configs
|
1069
1155
|
self.get_config()
|
@@ -1087,6 +1173,20 @@ class CLIManager:
|
|
1087
1173
|
default=True,
|
1088
1174
|
)
|
1089
1175
|
self.config[arg] = nc
|
1176
|
+
|
1177
|
+
elif arg == "rate_tolerance":
|
1178
|
+
while True:
|
1179
|
+
val = FloatPrompt.ask(
|
1180
|
+
f"What percentage would you like to set for [red]{arg}[/red]?\nValues are percentages (e.g. 0.05 for 5%)",
|
1181
|
+
default=0.05,
|
1182
|
+
)
|
1183
|
+
try:
|
1184
|
+
validated_val = validate_rate_tolerance(val)
|
1185
|
+
self.config[arg] = validated_val
|
1186
|
+
break
|
1187
|
+
except typer.BadParameter as e:
|
1188
|
+
print_error(str(e))
|
1189
|
+
continue
|
1090
1190
|
else:
|
1091
1191
|
val = Prompt.ask(
|
1092
1192
|
f"What value would you like to assign to [red]{arg}[/red]?"
|
@@ -1144,6 +1244,18 @@ class CLIManager:
|
|
1144
1244
|
wallet_hotkey: bool = typer.Option(False, *Options.wallet_hotkey.param_decls),
|
1145
1245
|
network: bool = typer.Option(False, *Options.network.param_decls),
|
1146
1246
|
use_cache: bool = typer.Option(False, "--cache"),
|
1247
|
+
rate_tolerance: bool = typer.Option(
|
1248
|
+
False, "--slippage", "--slippage-tolerance", "--tolerance"
|
1249
|
+
),
|
1250
|
+
safe_staking: bool = typer.Option(
|
1251
|
+
False, "--safe-staking/--no-safe-staking", "--safe/--unsafe"
|
1252
|
+
),
|
1253
|
+
allow_partial_stake: bool = typer.Option(
|
1254
|
+
False,
|
1255
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
1256
|
+
"--partial/--no-partial",
|
1257
|
+
"--allow/--not-allow",
|
1258
|
+
),
|
1147
1259
|
all_items: bool = typer.Option(False, "--all"),
|
1148
1260
|
):
|
1149
1261
|
"""
|
@@ -1173,6 +1285,9 @@ class CLIManager:
|
|
1173
1285
|
"wallet_hotkey": wallet_hotkey,
|
1174
1286
|
"network": network,
|
1175
1287
|
"use_cache": use_cache,
|
1288
|
+
"rate_tolerance": rate_tolerance,
|
1289
|
+
"safe_staking": safe_staking,
|
1290
|
+
"allow_partial_stake": allow_partial_stake,
|
1176
1291
|
}
|
1177
1292
|
|
1178
1293
|
# If no specific argument is provided, iterate over all
|
@@ -1234,6 +1349,8 @@ class CLIManager:
|
|
1234
1349
|
else:
|
1235
1350
|
if value in Constants.networks:
|
1236
1351
|
value = value + f" ({Constants.network_map[value]})"
|
1352
|
+
if key == "rate_tolerance":
|
1353
|
+
value = f"{value} ({value*100}%)" if value is not None else "None"
|
1237
1354
|
|
1238
1355
|
elif key in deprecated_configs:
|
1239
1356
|
continue
|
@@ -1246,13 +1363,112 @@ class CLIManager:
|
|
1246
1363
|
table.add_row(str(key), str(value), "")
|
1247
1364
|
|
1248
1365
|
console.print(table)
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1366
|
+
|
1367
|
+
def ask_rate_tolerance(
|
1368
|
+
self,
|
1369
|
+
rate_tolerance: Optional[float],
|
1370
|
+
) -> float:
|
1371
|
+
"""
|
1372
|
+
Gets rate tolerance from args, config, or default.
|
1373
|
+
|
1374
|
+
Args:
|
1375
|
+
rate_tolerance (Optional[float]): Explicitly provided slippage value
|
1376
|
+
|
1377
|
+
Returns:
|
1378
|
+
float: rate tolerance value
|
1379
|
+
"""
|
1380
|
+
if rate_tolerance is not None:
|
1381
|
+
console.print(
|
1382
|
+
f"[dim][blue]Rate tolerance[/blue]: [bold cyan]{rate_tolerance} ({rate_tolerance*100}%)[/bold cyan]."
|
1254
1383
|
)
|
1255
|
-
|
1384
|
+
return rate_tolerance
|
1385
|
+
elif self.config.get("rate_tolerance") is not None:
|
1386
|
+
config_slippage = self.config["rate_tolerance"]
|
1387
|
+
console.print(
|
1388
|
+
f"[dim][blue]Rate tolerance[/blue]: [bold cyan]{config_slippage} ({config_slippage*100}%)[/bold cyan] (from config)."
|
1389
|
+
)
|
1390
|
+
return config_slippage
|
1391
|
+
else:
|
1392
|
+
console.print(
|
1393
|
+
"[dim][blue]Rate tolerance[/blue]: "
|
1394
|
+
+ f"[bold cyan]{defaults.rate_tolerance} ({defaults.rate_tolerance*100}%)[/bold cyan] "
|
1395
|
+
+ "by default. Set this using "
|
1396
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1397
|
+
+ "or "
|
1398
|
+
+ "[dark_sea_green3 italic]`--tolerance`[/dark_sea_green3 italic] flag[/dim]"
|
1399
|
+
)
|
1400
|
+
return defaults.rate_tolerance
|
1401
|
+
|
1402
|
+
def ask_safe_staking(
|
1403
|
+
self,
|
1404
|
+
safe_staking: Optional[bool],
|
1405
|
+
) -> bool:
|
1406
|
+
"""
|
1407
|
+
Gets safe staking setting from args, config, or default.
|
1408
|
+
|
1409
|
+
Args:
|
1410
|
+
safe_staking (Optional[bool]): Explicitly provided safe staking value
|
1411
|
+
|
1412
|
+
Returns:
|
1413
|
+
bool: Safe staking setting
|
1414
|
+
"""
|
1415
|
+
if safe_staking is not None:
|
1416
|
+
console.print(
|
1417
|
+
f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan]."
|
1418
|
+
)
|
1419
|
+
return safe_staking
|
1420
|
+
elif self.config.get("safe_staking") is not None:
|
1421
|
+
safe_staking = self.config["safe_staking"]
|
1422
|
+
console.print(
|
1423
|
+
f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] (from config)."
|
1424
|
+
)
|
1425
|
+
return safe_staking
|
1426
|
+
else:
|
1427
|
+
safe_staking = True
|
1428
|
+
console.print(
|
1429
|
+
"[dim][blue]Safe staking[/blue]: "
|
1430
|
+
+ f"[bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] "
|
1431
|
+
+ "by default. Set this using "
|
1432
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1433
|
+
+ "or "
|
1434
|
+
+ "[dark_sea_green3 italic]`--safe/--unsafe`[/dark_sea_green3 italic] flag[/dim]"
|
1435
|
+
)
|
1436
|
+
return safe_staking
|
1437
|
+
|
1438
|
+
def ask_partial_stake(
|
1439
|
+
self,
|
1440
|
+
allow_partial_stake: Optional[bool],
|
1441
|
+
) -> bool:
|
1442
|
+
"""
|
1443
|
+
Gets partial stake setting from args, config, or default.
|
1444
|
+
|
1445
|
+
Args:
|
1446
|
+
allow_partial_stake (Optional[bool]): Explicitly provided partial stake value
|
1447
|
+
|
1448
|
+
Returns:
|
1449
|
+
bool: Partial stake setting
|
1450
|
+
"""
|
1451
|
+
if allow_partial_stake is not None:
|
1452
|
+
console.print(
|
1453
|
+
f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan]."
|
1454
|
+
)
|
1455
|
+
return allow_partial_stake
|
1456
|
+
elif self.config.get("allow_partial_stake") is not None:
|
1457
|
+
config_partial = self.config["allow_partial_stake"]
|
1458
|
+
console.print(
|
1459
|
+
f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if config_partial else 'disabled'}[/bold cyan] (from config)."
|
1460
|
+
)
|
1461
|
+
return config_partial
|
1462
|
+
else:
|
1463
|
+
console.print(
|
1464
|
+
"[dim][blue]Partial staking[/blue]: "
|
1465
|
+
+ f"[bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan] "
|
1466
|
+
+ "by default. Set this using "
|
1467
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1468
|
+
+ "or "
|
1469
|
+
+ "[dark_sea_green3 italic]`--partial/--no-partial`[/dark_sea_green3 italic] flag[/dim]"
|
1470
|
+
)
|
1471
|
+
return False
|
1256
1472
|
|
1257
1473
|
def wallet_ask(
|
1258
1474
|
self,
|
@@ -1410,50 +1626,9 @@ class CLIManager:
|
|
1410
1626
|
|
1411
1627
|
USAGE
|
1412
1628
|
|
1413
|
-
The command offers various options to customize the output. Users can filter the displayed data by specific
|
1414
|
-
netuid, sort by different criteria, and choose to include all the wallets in the user's wallet path location.
|
1415
|
-
The output is presented in a tabular format with the following columns:
|
1416
|
-
|
1417
|
-
- COLDKEY: The SS58 address of the coldkey.
|
1418
|
-
|
1419
|
-
- HOTKEY: The SS58 address of the hotkey.
|
1420
|
-
|
1421
|
-
- UID: Unique identifier of the neuron.
|
1422
|
-
|
1423
|
-
- ACTIVE: Indicates if the neuron is active.
|
1424
|
-
|
1425
|
-
- STAKE(τ): Amount of stake in the neuron, in TAO.
|
1426
|
-
|
1427
|
-
- RANK: The rank of the neuron within the network.
|
1428
|
-
|
1429
|
-
- TRUST: Trust score of the neuron.
|
1430
|
-
|
1431
|
-
- CONSENSUS: Consensus score of the neuron.
|
1432
|
-
|
1433
|
-
- INCENTIVE: Incentive score of the neuron.
|
1434
|
-
|
1435
|
-
- DIVIDENDS: Dividends earned by the neuron.
|
1436
|
-
|
1437
|
-
- EMISSION(p): Emission received by the neuron, expressed in rho.
|
1438
|
-
|
1439
|
-
- VTRUST: Validator trust score of the neuron.
|
1440
|
-
|
1441
|
-
- VPERMIT: Indicates if the neuron has a validator permit.
|
1442
|
-
|
1443
|
-
- UPDATED: Time since last update.
|
1444
|
-
|
1445
|
-
- AXON: IP address and port of the neuron.
|
1446
|
-
|
1447
|
-
- HOTKEY_SS58: Human-readable representation of the hotkey.
|
1448
|
-
|
1449
|
-
|
1450
|
-
# EXAMPLE:
|
1451
|
-
|
1452
1629
|
[green]$[/green] btcli wallet overview
|
1453
1630
|
|
1454
|
-
[green]$[/green] btcli wallet overview --all
|
1455
|
-
|
1456
|
-
[green]$[/green] btcli wallet overview -in hk1,hk2 --sort-by stake
|
1631
|
+
[green]$[/green] btcli wallet overview --all
|
1457
1632
|
|
1458
1633
|
[bold]NOTE[/bold]: This command is read-only and does not modify the blockchain state or account configuration.
|
1459
1634
|
It provides a quick and comprehensive view of the user's network presence, making it useful for monitoring account status,
|
@@ -2561,7 +2736,25 @@ class CLIManager:
|
|
2561
2736
|
no_prompt: bool = Options.prompt,
|
2562
2737
|
# TODO add: all-wallets, reuse_last, html_output
|
2563
2738
|
):
|
2564
|
-
"""
|
2739
|
+
"""
|
2740
|
+
Display detailed stake information for a wallet across all subnets.
|
2741
|
+
|
2742
|
+
Shows stake allocations, exchange rates, and emissions for each hotkey.
|
2743
|
+
|
2744
|
+
[bold]Common Examples:[/bold]
|
2745
|
+
|
2746
|
+
1. Basic stake overview:
|
2747
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet
|
2748
|
+
|
2749
|
+
2. Live updating view with refresh:
|
2750
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet --live
|
2751
|
+
|
2752
|
+
3. View specific coldkey by address:
|
2753
|
+
[green]$[/green] btcli stake list --ss58 5Dk...X3q
|
2754
|
+
|
2755
|
+
4. Verbose output with full values:
|
2756
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet --verbose
|
2757
|
+
"""
|
2565
2758
|
self.verbosity_handler(quiet, verbose)
|
2566
2759
|
|
2567
2760
|
wallet = None
|
@@ -2586,7 +2779,7 @@ class CLIManager:
|
|
2586
2779
|
)
|
2587
2780
|
|
2588
2781
|
return self._run_command(
|
2589
|
-
|
2782
|
+
list_stake.stake_list(
|
2590
2783
|
wallet,
|
2591
2784
|
coldkey_ss58,
|
2592
2785
|
self.initialize_chain(network),
|
@@ -2608,12 +2801,6 @@ class CLIManager:
|
|
2608
2801
|
amount: float = typer.Option(
|
2609
2802
|
0.0, "--amount", help="The amount of TAO to stake"
|
2610
2803
|
),
|
2611
|
-
max_stake: float = typer.Option(
|
2612
|
-
0.0,
|
2613
|
-
"--max-stake",
|
2614
|
-
"-m",
|
2615
|
-
help="Stake is sent to a hotkey only until the hotkey's total stake is less than or equal to this maximum staked TAO. If a hotkey already has stake greater than this amount, then stake is not added to this hotkey.",
|
2616
|
-
),
|
2617
2804
|
include_hotkeys: str = typer.Option(
|
2618
2805
|
"",
|
2619
2806
|
"--include-hotkeys",
|
@@ -2639,22 +2826,50 @@ class CLIManager:
|
|
2639
2826
|
wallet_path: str = Options.wallet_path,
|
2640
2827
|
wallet_hotkey: str = Options.wallet_hotkey,
|
2641
2828
|
network: Optional[list[str]] = Options.network,
|
2829
|
+
rate_tolerance: Optional[float] = Options.rate_tolerance,
|
2830
|
+
safe_staking: Optional[bool] = Options.safe_staking,
|
2831
|
+
allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
|
2642
2832
|
prompt: bool = Options.prompt,
|
2643
2833
|
quiet: bool = Options.quiet,
|
2644
2834
|
verbose: bool = Options.verbose,
|
2645
2835
|
):
|
2646
2836
|
"""
|
2647
|
-
Stake TAO to one or more hotkeys
|
2837
|
+
Stake TAO to one or more hotkeys on specific netuids with your coldkey.
|
2648
2838
|
|
2649
|
-
|
2839
|
+
Stake is always added through your coldkey's free balance. For stake movement, please see `[green]$[/green] btcli stake move` command.
|
2650
2840
|
|
2651
|
-
|
2841
|
+
[bold]Common Examples:[/bold]
|
2652
2842
|
|
2653
|
-
|
2843
|
+
1. Interactive staking (guided prompts):
|
2844
|
+
[green]$[/green] btcli stake add
|
2845
|
+
|
2846
|
+
2. Safe staking with rate tolerance of 10% with partial transaction disabled:
|
2847
|
+
[green]$[/green] btcli stake add --amount 100 --netuid 1 --safe --tolerance 0.1 --no-partial
|
2848
|
+
|
2849
|
+
3. Allow partial stake if rates change with tolerance of 10%:
|
2850
|
+
[green]$[/green] btcli stake add --amount 300 --safe --partial --tolerance 0.1
|
2851
|
+
|
2852
|
+
4. Unsafe staking with no rate protection:
|
2853
|
+
[green]$[/green] btcli stake add --amount 300 --netuid 1 --unsafe
|
2854
|
+
|
2855
|
+
5. Stake to multiple hotkeys:
|
2856
|
+
[green]$[/green] btcli stake add --amount 200 --include-hotkeys hk_ss58_1,hk_ss58_2,hk_ss58_3
|
2857
|
+
|
2858
|
+
6. Stake all balance to a subnet:
|
2859
|
+
[green]$[/green] btcli stake add --all --netuid 3
|
2860
|
+
|
2861
|
+
[bold]Safe Staking Parameters:[/bold]
|
2862
|
+
• [blue]--safe[/blue]: Enables rate tolerance checks
|
2863
|
+
• [blue]--tolerance[/blue]: Maximum % rate change allowed (0.05 = 5%)
|
2864
|
+
• [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
|
2654
2865
|
|
2655
|
-
[green]$[/green] btcli stake add --amount 100 --wallet-name <my_wallet> --wallet-hotkey <my_hotkey>
|
2656
2866
|
"""
|
2657
2867
|
self.verbosity_handler(quiet, verbose)
|
2868
|
+
safe_staking = self.ask_safe_staking(safe_staking)
|
2869
|
+
if safe_staking:
|
2870
|
+
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
|
2871
|
+
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
|
2872
|
+
console.print("\n")
|
2658
2873
|
netuid = get_optional_netuid(netuid, all_netuids)
|
2659
2874
|
|
2660
2875
|
if stake_all and amount:
|
@@ -2764,8 +2979,8 @@ class CLIManager:
|
|
2764
2979
|
excluded_hotkeys = []
|
2765
2980
|
|
2766
2981
|
# TODO: Ask amount for each subnet explicitly if more than one
|
2767
|
-
if not stake_all and not amount
|
2768
|
-
free_balance
|
2982
|
+
if not stake_all and not amount:
|
2983
|
+
free_balance = self._run_command(
|
2769
2984
|
wallets.wallet_balance(
|
2770
2985
|
wallet, self.initialize_chain(network), False, None
|
2771
2986
|
),
|
@@ -2793,18 +3008,19 @@ class CLIManager:
|
|
2793
3008
|
raise typer.Exit()
|
2794
3009
|
|
2795
3010
|
return self._run_command(
|
2796
|
-
|
3011
|
+
add_stake.stake_add(
|
2797
3012
|
wallet,
|
2798
3013
|
self.initialize_chain(network),
|
2799
3014
|
netuid,
|
2800
3015
|
stake_all,
|
2801
3016
|
amount,
|
2802
|
-
False,
|
2803
3017
|
prompt,
|
2804
|
-
max_stake,
|
2805
3018
|
all_hotkeys,
|
2806
3019
|
included_hotkeys,
|
2807
3020
|
excluded_hotkeys,
|
3021
|
+
safe_staking,
|
3022
|
+
rate_tolerance,
|
3023
|
+
allow_partial_stake,
|
2808
3024
|
)
|
2809
3025
|
)
|
2810
3026
|
|
@@ -2837,12 +3053,6 @@ class CLIManager:
|
|
2837
3053
|
"",
|
2838
3054
|
help="The ss58 address of the hotkey to unstake from.",
|
2839
3055
|
),
|
2840
|
-
keep_stake: float = typer.Option(
|
2841
|
-
0.0,
|
2842
|
-
"--keep-stake",
|
2843
|
-
"--keep",
|
2844
|
-
help="Sets the maximum amount of TAO to remain staked in each hotkey.",
|
2845
|
-
),
|
2846
3056
|
include_hotkeys: str = typer.Option(
|
2847
3057
|
"",
|
2848
3058
|
"--include-hotkeys",
|
@@ -2861,6 +3071,9 @@ class CLIManager:
|
|
2861
3071
|
help="When set, this command unstakes from all the hotkeys associated with the wallet. Do not use if specifying "
|
2862
3072
|
"hotkeys in `--include-hotkeys`.",
|
2863
3073
|
),
|
3074
|
+
rate_tolerance: Optional[float] = Options.rate_tolerance,
|
3075
|
+
safe_staking: Optional[bool] = Options.safe_staking,
|
3076
|
+
allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
|
2864
3077
|
prompt: bool = Options.prompt,
|
2865
3078
|
interactive: bool = typer.Option(
|
2866
3079
|
False,
|
@@ -2872,20 +3085,42 @@ class CLIManager:
|
|
2872
3085
|
verbose: bool = Options.verbose,
|
2873
3086
|
):
|
2874
3087
|
"""
|
2875
|
-
Unstake TAO from one or more hotkeys and transfer them back to the user's coldkey.
|
3088
|
+
Unstake TAO from one or more hotkeys and transfer them back to the user's coldkey wallet.
|
2876
3089
|
|
2877
|
-
This command is used to withdraw TAO
|
3090
|
+
This command is used to withdraw TAO or Alpha stake from different hotkeys.
|
2878
3091
|
|
2879
|
-
|
3092
|
+
[bold]Common Examples:[/bold]
|
3093
|
+
|
3094
|
+
1. Interactive unstaking (guided prompts):
|
3095
|
+
[green]$[/green] btcli stake remove
|
3096
|
+
|
3097
|
+
2. Safe unstaking with 10% rate tolerance:
|
3098
|
+
[green]$[/green] btcli stake remove --amount 100 --netuid 1 --safe --tolerance 0.1
|
3099
|
+
|
3100
|
+
3. Allow partial unstake if rates change:
|
3101
|
+
[green]$[/green] btcli stake remove --amount 300 --safe --partial
|
3102
|
+
|
3103
|
+
4. Unstake from multiple hotkeys:
|
3104
|
+
[green]$[/green] btcli stake remove --amount 200 --include-hotkeys hk1,hk2,hk3
|
3105
|
+
|
3106
|
+
5. Unstake all from a hotkey:
|
3107
|
+
[green]$[/green] btcli stake remove --all
|
2880
3108
|
|
2881
|
-
|
3109
|
+
6. Unstake all Alpha from a hotkey and stake to Root:
|
3110
|
+
[green]$[/green] btcli stake remove --all-alpha
|
2882
3111
|
|
2883
|
-
[
|
3112
|
+
[bold]Safe Staking Parameters:[/bold]
|
3113
|
+
• [blue]--safe[/blue]: Enables rate tolerance checks during unstaking
|
3114
|
+
• [blue]--tolerance[/blue]: Max allowed rate change (0.05 = 5%)
|
3115
|
+
• [blue]--partial[/blue]: Complete partial unstake if rates exceed tolerance
|
2884
3116
|
"""
|
2885
3117
|
self.verbosity_handler(quiet, verbose)
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
3118
|
+
if not unstake_all and not unstake_all_alpha:
|
3119
|
+
safe_staking = self.ask_safe_staking(safe_staking)
|
3120
|
+
if safe_staking:
|
3121
|
+
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
|
3122
|
+
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
|
3123
|
+
console.print("\n")
|
2889
3124
|
|
2890
3125
|
if interactive and any(
|
2891
3126
|
[hotkey_ss58_address, include_hotkeys, exclude_hotkeys, all_hotkeys]
|
@@ -2961,6 +3196,52 @@ class CLIManager:
|
|
2961
3196
|
validate=WV.WALLET_AND_HOTKEY,
|
2962
3197
|
)
|
2963
3198
|
|
3199
|
+
elif unstake_all or unstake_all_alpha:
|
3200
|
+
if not wallet_name:
|
3201
|
+
wallet_name = Prompt.ask(
|
3202
|
+
"Enter the [blue]wallet name[/blue]",
|
3203
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
3204
|
+
)
|
3205
|
+
if include_hotkeys:
|
3206
|
+
if len(include_hotkeys) > 1:
|
3207
|
+
print_error("Cannot unstake_all from multiple hotkeys at once.")
|
3208
|
+
raise typer.Exit()
|
3209
|
+
elif is_valid_ss58_address(include_hotkeys[0]):
|
3210
|
+
hotkey_ss58_address = include_hotkeys[0]
|
3211
|
+
else:
|
3212
|
+
print_error("Invalid hotkey ss58 address.")
|
3213
|
+
raise typer.Exit()
|
3214
|
+
else:
|
3215
|
+
hotkey_or_ss58 = Prompt.ask(
|
3216
|
+
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from",
|
3217
|
+
default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
|
3218
|
+
)
|
3219
|
+
if is_valid_ss58_address(hotkey_or_ss58):
|
3220
|
+
hotkey_ss58_address = hotkey_or_ss58
|
3221
|
+
wallet = self.wallet_ask(
|
3222
|
+
wallet_name,
|
3223
|
+
wallet_path,
|
3224
|
+
wallet_hotkey,
|
3225
|
+
ask_for=[WO.NAME, WO.PATH],
|
3226
|
+
)
|
3227
|
+
else:
|
3228
|
+
wallet_hotkey = hotkey_or_ss58
|
3229
|
+
wallet = self.wallet_ask(
|
3230
|
+
wallet_name,
|
3231
|
+
wallet_path,
|
3232
|
+
wallet_hotkey,
|
3233
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3234
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3235
|
+
)
|
3236
|
+
return self._run_command(
|
3237
|
+
remove_stake.unstake_all(
|
3238
|
+
wallet=wallet,
|
3239
|
+
subtensor=self.initialize_chain(network),
|
3240
|
+
hotkey_ss58_address=hotkey_ss58_address,
|
3241
|
+
unstake_all_alpha=unstake_all_alpha,
|
3242
|
+
prompt=prompt,
|
3243
|
+
)
|
3244
|
+
)
|
2964
3245
|
elif (
|
2965
3246
|
all_hotkeys
|
2966
3247
|
or include_hotkeys
|
@@ -3003,20 +3284,20 @@ class CLIManager:
|
|
3003
3284
|
excluded_hotkeys = []
|
3004
3285
|
|
3005
3286
|
return self._run_command(
|
3006
|
-
|
3007
|
-
wallet,
|
3008
|
-
self.initialize_chain(network),
|
3009
|
-
hotkey_ss58_address,
|
3010
|
-
all_hotkeys,
|
3011
|
-
included_hotkeys,
|
3012
|
-
excluded_hotkeys,
|
3013
|
-
amount,
|
3014
|
-
|
3015
|
-
|
3016
|
-
prompt,
|
3017
|
-
interactive,
|
3287
|
+
remove_stake.unstake(
|
3288
|
+
wallet=wallet,
|
3289
|
+
subtensor=self.initialize_chain(network),
|
3290
|
+
hotkey_ss58_address=hotkey_ss58_address,
|
3291
|
+
all_hotkeys=all_hotkeys,
|
3292
|
+
include_hotkeys=included_hotkeys,
|
3293
|
+
exclude_hotkeys=excluded_hotkeys,
|
3294
|
+
amount=amount,
|
3295
|
+
prompt=prompt,
|
3296
|
+
interactive=interactive,
|
3018
3297
|
netuid=netuid,
|
3019
|
-
|
3298
|
+
safe_staking=safe_staking,
|
3299
|
+
rate_tolerance=rate_tolerance,
|
3300
|
+
allow_partial_stake=allow_partial_stake,
|
3020
3301
|
)
|
3021
3302
|
)
|
3022
3303
|
|
@@ -3159,7 +3440,7 @@ class CLIManager:
|
|
3159
3440
|
)
|
3160
3441
|
|
3161
3442
|
return self._run_command(
|
3162
|
-
|
3443
|
+
move_stake.move_stake(
|
3163
3444
|
subtensor=self.initialize_chain(network),
|
3164
3445
|
wallet=wallet,
|
3165
3446
|
origin_netuid=origin_netuid,
|
@@ -3277,7 +3558,7 @@ class CLIManager:
|
|
3277
3558
|
)
|
3278
3559
|
|
3279
3560
|
return self._run_command(
|
3280
|
-
|
3561
|
+
move_stake.transfer_stake(
|
3281
3562
|
wallet=wallet,
|
3282
3563
|
subtensor=self.initialize_chain(network),
|
3283
3564
|
origin_netuid=origin_netuid,
|
@@ -3376,7 +3657,7 @@ class CLIManager:
|
|
3376
3657
|
amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
|
3377
3658
|
|
3378
3659
|
return self._run_command(
|
3379
|
-
|
3660
|
+
move_stake.swap_stake(
|
3380
3661
|
wallet=wallet,
|
3381
3662
|
subtensor=self.initialize_chain(network),
|
3382
3663
|
origin_netuid=origin_netuid,
|
@@ -3737,8 +4018,6 @@ class CLIManager:
|
|
3737
4018
|
"""
|
3738
4019
|
Shows a list of the hyperparameters for the specified subnet.
|
3739
4020
|
|
3740
|
-
The output of this command is the same as that of `btcli subnets hyperparameters`.
|
3741
|
-
|
3742
4021
|
EXAMPLE
|
3743
4022
|
|
3744
4023
|
[green]$[/green] btcli sudo get --netuid 1
|
@@ -3914,40 +4193,37 @@ class CLIManager:
|
|
3914
4193
|
def subnets_list(
|
3915
4194
|
self,
|
3916
4195
|
network: Optional[list[str]] = Options.network,
|
3917
|
-
# reuse_last: bool = Options.reuse_last,
|
3918
|
-
# html_output: bool = Options.html_output,
|
3919
4196
|
quiet: bool = Options.quiet,
|
3920
4197
|
verbose: bool = Options.verbose,
|
3921
4198
|
live_mode: bool = Options.live,
|
3922
4199
|
):
|
3923
4200
|
"""
|
3924
|
-
|
4201
|
+
List all subnets and their detailed information.
|
3925
4202
|
|
3926
|
-
|
4203
|
+
[bold]Common Examples:[/bold]
|
3927
4204
|
|
3928
|
-
|
3929
|
-
|
3930
|
-
- MAX_N: The maximum allowed number of neurons in the subnet.
|
3931
|
-
- EMISSION: The percentage of emissions to the subnet as of the last tempo.
|
3932
|
-
- TEMPO: The subnet's tempo, expressed in number of blocks.
|
3933
|
-
- RECYCLE: The recycle register cost for this subnet.
|
3934
|
-
- POW: The proof of work (PoW) difficulty.
|
3935
|
-
- SUDO: The subnet owner's name or the owner's ss58 address.
|
4205
|
+
1. List all subnets:
|
4206
|
+
[green]$[/green] btcli subnets list
|
3936
4207
|
|
3937
|
-
|
4208
|
+
2. List all subnets in live mode:
|
4209
|
+
[green]$[/green] btcli subnets list --live
|
3938
4210
|
|
3939
|
-
[
|
4211
|
+
[bold]Output Columns:[/bold]
|
4212
|
+
• [white]Netuid[/white] - Subnet identifier number
|
4213
|
+
• [white]Name[/white] - Subnet name with currency symbol (τ/α/β etc)
|
4214
|
+
• [white]Price (τ_in/α_in)[/white] - Exchange rate (TAO per alpha token)
|
4215
|
+
• [white]Market Cap (α * Price)[/white] - Total value in TAO (alpha tokens × price)
|
4216
|
+
• [white]Emission (τ)[/white] - TAO rewards emitted per block to subnet
|
4217
|
+
• [white]P (τ_in, α_in)[/white] - Pool reserves (Tao reserves, alpha reserves) in liquidity pool
|
4218
|
+
• [white]Stake (α_out)[/white] - Total staked alpha tokens across all hotkeys (alpha outstanding)
|
4219
|
+
• [white]Supply (α)[/white] - Circulating alpha token supply
|
4220
|
+
• [white]Tempo (k/n)[/white] - Block interval for subnet updates
|
4221
|
+
|
4222
|
+
EXAMPLE
|
4223
|
+
|
4224
|
+
[green]$[/green] btcli subnets list
|
3940
4225
|
"""
|
3941
4226
|
self.verbosity_handler(quiet, verbose)
|
3942
|
-
# if (reuse_last or html_output) and self.config.get("use_cache") is False:
|
3943
|
-
# err_console.print(
|
3944
|
-
# "Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'. "
|
3945
|
-
# "Change the config to 'False' using `btcli config set`."
|
3946
|
-
# )
|
3947
|
-
# raise typer.Exit()
|
3948
|
-
# if reuse_last:
|
3949
|
-
# subtensor = None
|
3950
|
-
# else:
|
3951
4227
|
subtensor = self.initialize_chain(network)
|
3952
4228
|
return self._run_command(
|
3953
4229
|
subnets.subnets_list(
|
@@ -4127,11 +4403,18 @@ class CLIManager:
|
|
4127
4403
|
verbose: bool = Options.verbose,
|
4128
4404
|
):
|
4129
4405
|
"""
|
4130
|
-
Registers a new subnet.
|
4406
|
+
Registers a new subnet on the network.
|
4131
4407
|
|
4132
|
-
|
4408
|
+
This command allows you to create a new subnet and set the subnet's identity.
|
4409
|
+
You also have the option to set your own identity after the registration is complete.
|
4133
4410
|
|
4411
|
+
[bold]Common Examples:[/bold]
|
4412
|
+
|
4413
|
+
1. Interactive subnet creation:
|
4134
4414
|
[green]$[/green] btcli subnets create
|
4415
|
+
|
4416
|
+
2. Create with GitHub repo and contact email:
|
4417
|
+
[green]$[/green] btcli subnets create --subnet-name MySubnet --github-repo https://github.com/myorg/mysubnet --subnet-contact team@mysubnet.net
|
4135
4418
|
"""
|
4136
4419
|
self.verbosity_handler(quiet, verbose)
|
4137
4420
|
wallet = self.wallet_ask(
|