bittensor-cli 9.1.0__py3-none-any.whl → 9.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bittensor_cli/cli.py +136 -53
- bittensor_cli/src/__init__.py +3 -0
- bittensor_cli/src/bittensor/subtensor_interface.py +1 -1
- bittensor_cli/src/bittensor/utils.py +55 -40
- bittensor_cli/src/commands/stake/add.py +1 -3
- bittensor_cli/src/commands/stake/list.py +1 -2
- bittensor_cli/src/commands/stake/move.py +80 -144
- bittensor_cli/src/commands/stake/remove.py +13 -12
- bittensor_cli/src/commands/subnets/subnets.py +7 -9
- bittensor_cli/src/commands/sudo.py +2 -3
- bittensor_cli/src/commands/view.py +106 -37
- bittensor_cli/src/commands/wallets.py +1 -11
- bittensor_cli/version.py +1 -1
- {bittensor_cli-9.1.0.dist-info → bittensor_cli-9.1.2.dist-info}/METADATA +10 -26
- {bittensor_cli-9.1.0.dist-info → bittensor_cli-9.1.2.dist-info}/RECORD +18 -19
- bittensor_cli/src/bittensor/templates/table.j2 +0 -267
- {bittensor_cli-9.1.0.dist-info → bittensor_cli-9.1.2.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.1.0.dist-info → bittensor_cli-9.1.2.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.1.0.dist-info → bittensor_cli-9.1.2.dist-info}/top_level.txt +0 -0
bittensor_cli/cli.py
CHANGED
@@ -109,6 +109,18 @@ class Options:
|
|
109
109
|
"--wallet.hotkey",
|
110
110
|
help="Hotkey of the wallet",
|
111
111
|
)
|
112
|
+
wallet_hotkey_ss58 = typer.Option(
|
113
|
+
None,
|
114
|
+
"--hotkey",
|
115
|
+
"--hotkey-ss58",
|
116
|
+
"-H",
|
117
|
+
"--wallet_hotkey",
|
118
|
+
"--wallet_hotkey_ss58",
|
119
|
+
"--wallet-hotkey",
|
120
|
+
"--wallet-hotkey-ss58",
|
121
|
+
"--wallet.hotkey",
|
122
|
+
help="Hotkey name or SS58 address of the hotkey",
|
123
|
+
)
|
112
124
|
mnemonic = typer.Option(
|
113
125
|
None,
|
114
126
|
help='Mnemonic used to regenerate your key. For example: "horse cart dog ..."',
|
@@ -246,6 +258,15 @@ class Options:
|
|
246
258
|
"--allow-partial/--not-partial",
|
247
259
|
help="Enable or disable partial stake mode (default: disabled).",
|
248
260
|
)
|
261
|
+
dashboard_path = typer.Option(
|
262
|
+
None,
|
263
|
+
"--dashboard-path",
|
264
|
+
"--dashboard_path",
|
265
|
+
"--dash_path",
|
266
|
+
"--dash.path",
|
267
|
+
"--dashboard.path",
|
268
|
+
help="Path to save the dashboard HTML file. For example: `~/.bittensor/dashboard`.",
|
269
|
+
)
|
249
270
|
|
250
271
|
|
251
272
|
def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
|
@@ -525,6 +546,7 @@ class CLIManager:
|
|
525
546
|
"rate_tolerance": None,
|
526
547
|
"safe_staking": True,
|
527
548
|
"allow_partial_stake": False,
|
549
|
+
"dashboard_path": None,
|
528
550
|
# Commenting this out as this needs to get updated
|
529
551
|
# "metagraph_cols": {
|
530
552
|
# "UID": True,
|
@@ -1119,6 +1141,7 @@ class CLIManager:
|
|
1119
1141
|
"--partial/--no-partial",
|
1120
1142
|
"--allow/--not-allow",
|
1121
1143
|
),
|
1144
|
+
dashboard_path: Optional[str] = Options.dashboard_path,
|
1122
1145
|
):
|
1123
1146
|
"""
|
1124
1147
|
Sets or updates configuration values in the BTCLI config file.
|
@@ -1148,6 +1171,7 @@ class CLIManager:
|
|
1148
1171
|
"rate_tolerance": rate_tolerance,
|
1149
1172
|
"safe_staking": safe_staking,
|
1150
1173
|
"allow_partial_stake": allow_partial_stake,
|
1174
|
+
"dashboard_path": dashboard_path,
|
1151
1175
|
}
|
1152
1176
|
bools = ["use_cache", "safe_staking", "allow_partial_stake"]
|
1153
1177
|
if all(v is None for v in args.values()):
|
@@ -1257,6 +1281,7 @@ class CLIManager:
|
|
1257
1281
|
"--allow/--not-allow",
|
1258
1282
|
),
|
1259
1283
|
all_items: bool = typer.Option(False, "--all"),
|
1284
|
+
dashboard_path: Optional[str] = Options.dashboard_path,
|
1260
1285
|
):
|
1261
1286
|
"""
|
1262
1287
|
Clears the fields in the config file and sets them to 'None'.
|
@@ -1288,6 +1313,7 @@ class CLIManager:
|
|
1288
1313
|
"rate_tolerance": rate_tolerance,
|
1289
1314
|
"safe_staking": safe_staking,
|
1290
1315
|
"allow_partial_stake": allow_partial_stake,
|
1316
|
+
"dashboard_path": dashboard_path,
|
1291
1317
|
}
|
1292
1318
|
|
1293
1319
|
# If no specific argument is provided, iterate over all
|
@@ -2656,23 +2682,29 @@ class CLIManager:
|
|
2656
2682
|
[bold]Note[/bold]: This command is primarily used for informational purposes and has no side effects on the blockchain network state.
|
2657
2683
|
"""
|
2658
2684
|
wallet = None
|
2659
|
-
if
|
2660
|
-
if
|
2661
|
-
|
2662
|
-
|
2663
|
-
|
2664
|
-
coldkey_or_ss58 = Prompt.ask(
|
2665
|
-
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
|
2666
|
-
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2667
|
-
)
|
2668
|
-
if is_valid_ss58_address(coldkey_or_ss58):
|
2669
|
-
coldkey_ss58 = coldkey_or_ss58
|
2685
|
+
if not wallet_name:
|
2686
|
+
if coldkey_ss58:
|
2687
|
+
if not is_valid_ss58_address(coldkey_ss58):
|
2688
|
+
print_error("You entered an invalid ss58 address")
|
2689
|
+
raise typer.Exit()
|
2670
2690
|
else:
|
2671
|
-
|
2672
|
-
|
2673
|
-
wallet_name
|
2691
|
+
coldkey_or_ss58 = Prompt.ask(
|
2692
|
+
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
|
2693
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2674
2694
|
)
|
2675
|
-
|
2695
|
+
if is_valid_ss58_address(coldkey_or_ss58):
|
2696
|
+
coldkey_ss58 = coldkey_or_ss58
|
2697
|
+
else:
|
2698
|
+
wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
|
2699
|
+
wallet = self.wallet_ask(
|
2700
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
2701
|
+
)
|
2702
|
+
coldkey_ss58 = wallet.coldkeypub.ss58_address
|
2703
|
+
else:
|
2704
|
+
wallet = self.wallet_ask(
|
2705
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
2706
|
+
)
|
2707
|
+
coldkey_ss58 = wallet.coldkeypub.ss58_address
|
2676
2708
|
|
2677
2709
|
self.verbosity_handler(quiet, verbose)
|
2678
2710
|
return self._run_command(
|
@@ -2937,7 +2969,7 @@ class CLIManager:
|
|
2937
2969
|
),
|
2938
2970
|
exit_early=False,
|
2939
2971
|
)
|
2940
|
-
if selected_hotkey
|
2972
|
+
if not selected_hotkey:
|
2941
2973
|
print_error("No delegate selected. Exiting.")
|
2942
2974
|
raise typer.Exit()
|
2943
2975
|
include_hotkeys = selected_hotkey
|
@@ -3048,14 +3080,12 @@ class CLIManager:
|
|
3048
3080
|
False,
|
3049
3081
|
"--unstake-all",
|
3050
3082
|
"--all",
|
3051
|
-
hidden=True,
|
3052
3083
|
help="When set, this command unstakes all staked TAO + Alpha from the all hotkeys.",
|
3053
3084
|
),
|
3054
3085
|
unstake_all_alpha: bool = typer.Option(
|
3055
3086
|
False,
|
3056
3087
|
"--unstake-all-alpha",
|
3057
3088
|
"--all-alpha",
|
3058
|
-
hidden=True,
|
3059
3089
|
help="When set, this command unstakes all staked Alpha from the all hotkeys.",
|
3060
3090
|
),
|
3061
3091
|
amount: float = typer.Option(
|
@@ -3337,7 +3367,7 @@ class CLIManager:
|
|
3337
3367
|
network: Optional[list[str]] = Options.network,
|
3338
3368
|
wallet_name: Optional[str] = Options.wallet_name,
|
3339
3369
|
wallet_path: Optional[str] = Options.wallet_path,
|
3340
|
-
wallet_hotkey: Optional[str] = Options.
|
3370
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
|
3341
3371
|
origin_netuid: Optional[int] = typer.Option(
|
3342
3372
|
None, "--origin-netuid", help="Origin netuid"
|
3343
3373
|
),
|
@@ -3357,6 +3387,8 @@ class CLIManager:
|
|
3357
3387
|
False, "--stake-all", "--all", help="Stake all", prompt=False
|
3358
3388
|
),
|
3359
3389
|
prompt: bool = Options.prompt,
|
3390
|
+
quiet: bool = Options.quiet,
|
3391
|
+
verbose: bool = Options.verbose,
|
3360
3392
|
):
|
3361
3393
|
"""
|
3362
3394
|
Move staked TAO between hotkeys while keeping the same coldkey ownership.
|
@@ -3378,6 +3410,7 @@ class CLIManager:
|
|
3378
3410
|
|
3379
3411
|
[green]$[/green] btcli stake move
|
3380
3412
|
"""
|
3413
|
+
self.verbosity_handler(quiet, verbose)
|
3381
3414
|
console.print(
|
3382
3415
|
"[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
|
3383
3416
|
)
|
@@ -3490,7 +3523,7 @@ class CLIManager:
|
|
3490
3523
|
network: Optional[list[str]] = Options.network,
|
3491
3524
|
wallet_name: Optional[str] = Options.wallet_name,
|
3492
3525
|
wallet_path: Optional[str] = Options.wallet_path,
|
3493
|
-
wallet_hotkey: Optional[str] = Options.
|
3526
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
|
3494
3527
|
origin_netuid: Optional[int] = typer.Option(
|
3495
3528
|
None,
|
3496
3529
|
"--origin-netuid",
|
@@ -3514,6 +3547,9 @@ class CLIManager:
|
|
3514
3547
|
"-a",
|
3515
3548
|
help="Amount of stake to transfer",
|
3516
3549
|
),
|
3550
|
+
stake_all: bool = typer.Option(
|
3551
|
+
False, "--stake-all", "--all", help="Stake all", prompt=False
|
3552
|
+
),
|
3517
3553
|
prompt: bool = Options.prompt,
|
3518
3554
|
quiet: bool = Options.quiet,
|
3519
3555
|
verbose: bool = Options.verbose,
|
@@ -3531,6 +3567,8 @@ class CLIManager:
|
|
3531
3567
|
- The destination subnet (--dest-netuid)
|
3532
3568
|
- The destination wallet/address (--dest)
|
3533
3569
|
- The amount to transfer (--amount)
|
3570
|
+
- The origin wallet (--wallet-name)
|
3571
|
+
- The origin hotkey wallet/address (--wallet-hotkey)
|
3534
3572
|
|
3535
3573
|
If no arguments are provided, an interactive selection menu will be shown.
|
3536
3574
|
|
@@ -3539,14 +3577,37 @@ class CLIManager:
|
|
3539
3577
|
Transfer 100 TAO from subnet 1 to subnet 2:
|
3540
3578
|
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest wallet2 --amount 100
|
3541
3579
|
|
3542
|
-
Using SS58 address:
|
3580
|
+
Using Destination SS58 address:
|
3543
3581
|
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --amount 100
|
3582
|
+
|
3583
|
+
Using Origin hotkey SS58 address (useful when transferring stake from a delegate):
|
3584
|
+
[green]$[/green] btcli stake transfer --wallet-hotkey 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --wallet-name sample_wallet
|
3585
|
+
|
3586
|
+
Transfer all available stake from origin hotkey:
|
3587
|
+
[green]$[/green] btcli stake transfer --all --origin-netuid 1 --dest-netuid 2
|
3544
3588
|
"""
|
3545
3589
|
console.print(
|
3546
3590
|
"[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
|
3547
3591
|
)
|
3548
3592
|
self.verbosity_handler(quiet, verbose)
|
3549
3593
|
|
3594
|
+
if not dest_ss58:
|
3595
|
+
dest_ss58 = Prompt.ask(
|
3596
|
+
"Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
|
3597
|
+
)
|
3598
|
+
|
3599
|
+
if is_valid_ss58_address(dest_ss58):
|
3600
|
+
dest_ss58 = dest_ss58
|
3601
|
+
else:
|
3602
|
+
dest_wallet = self.wallet_ask(
|
3603
|
+
dest_ss58,
|
3604
|
+
wallet_path,
|
3605
|
+
None,
|
3606
|
+
ask_for=[WO.NAME, WO.PATH],
|
3607
|
+
validate=WV.WALLET,
|
3608
|
+
)
|
3609
|
+
dest_ss58 = dest_wallet.coldkeypub.ss58_address
|
3610
|
+
|
3550
3611
|
if not wallet_name:
|
3551
3612
|
wallet_name = Prompt.ask(
|
3552
3613
|
"Enter the [blue]origin wallet name[/blue]",
|
@@ -3556,13 +3617,16 @@ class CLIManager:
|
|
3556
3617
|
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
3557
3618
|
)
|
3558
3619
|
|
3620
|
+
interactive_selection = False
|
3559
3621
|
if not wallet_hotkey:
|
3560
3622
|
origin_hotkey = Prompt.ask(
|
3561
|
-
"Enter the [blue]origin hotkey[/blue] name or "
|
3562
|
-
"[
|
3563
|
-
default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
|
3623
|
+
"Enter the [blue]origin hotkey[/blue] name or ss58 address [bold](stake will be transferred FROM here)[/bold] "
|
3624
|
+
"[dim](or press Enter to select from existing stakes)[/dim]"
|
3564
3625
|
)
|
3565
|
-
if
|
3626
|
+
if origin_hotkey == "":
|
3627
|
+
interactive_selection = True
|
3628
|
+
|
3629
|
+
elif is_valid_ss58_address(origin_hotkey):
|
3566
3630
|
origin_hotkey = origin_hotkey
|
3567
3631
|
else:
|
3568
3632
|
wallet = self.wallet_ask(
|
@@ -3586,33 +3650,11 @@ class CLIManager:
|
|
3586
3650
|
)
|
3587
3651
|
origin_hotkey = wallet.hotkey.ss58_address
|
3588
3652
|
|
3589
|
-
if not
|
3590
|
-
dest_ss58 = Prompt.ask(
|
3591
|
-
"Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
|
3592
|
-
)
|
3593
|
-
|
3594
|
-
if is_valid_ss58_address(dest_ss58):
|
3595
|
-
dest_ss58 = dest_ss58
|
3596
|
-
else:
|
3597
|
-
dest_wallet = self.wallet_ask(
|
3598
|
-
dest_ss58,
|
3599
|
-
wallet_path,
|
3600
|
-
None,
|
3601
|
-
ask_for=[WO.NAME, WO.PATH],
|
3602
|
-
validate=WV.WALLET,
|
3603
|
-
)
|
3604
|
-
dest_ss58 = dest_wallet.coldkeypub.ss58_address
|
3605
|
-
|
3606
|
-
interactive_selection = False
|
3607
|
-
if origin_netuid is None and dest_netuid is None and not amount:
|
3608
|
-
interactive_selection = True
|
3609
|
-
else:
|
3653
|
+
if not interactive_selection:
|
3610
3654
|
if origin_netuid is None:
|
3611
3655
|
origin_netuid = IntPrompt.ask(
|
3612
3656
|
"Enter the [blue]origin subnet[/blue] (netuid)"
|
3613
3657
|
)
|
3614
|
-
if not amount:
|
3615
|
-
amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to transfer")
|
3616
3658
|
|
3617
3659
|
if dest_netuid is None:
|
3618
3660
|
dest_netuid = IntPrompt.ask(
|
@@ -3629,6 +3671,7 @@ class CLIManager:
|
|
3629
3671
|
dest_coldkey_ss58=dest_ss58,
|
3630
3672
|
amount=amount,
|
3631
3673
|
interactive_selection=interactive_selection,
|
3674
|
+
stake_all=stake_all,
|
3632
3675
|
prompt=prompt,
|
3633
3676
|
)
|
3634
3677
|
)
|
@@ -5091,6 +5134,19 @@ class CLIManager:
|
|
5091
5134
|
wallet_name: str = Options.wallet_name,
|
5092
5135
|
wallet_path: str = Options.wallet_path,
|
5093
5136
|
wallet_hotkey: str = Options.wallet_hotkey,
|
5137
|
+
coldkey_ss58: Optional[str] = typer.Option(
|
5138
|
+
None,
|
5139
|
+
"--coldkey-ss58",
|
5140
|
+
"--ss58",
|
5141
|
+
help="Coldkey SS58 address to view dashboard for",
|
5142
|
+
),
|
5143
|
+
use_wry: bool = typer.Option(
|
5144
|
+
False, "--use-wry", help="Use PyWry instead of browser window"
|
5145
|
+
),
|
5146
|
+
save_file: bool = typer.Option(
|
5147
|
+
False, "--save-file", "--save", help="Save the dashboard HTML file"
|
5148
|
+
),
|
5149
|
+
dashboard_path: Optional[str] = Options.dashboard_path,
|
5094
5150
|
quiet: bool = Options.quiet,
|
5095
5151
|
verbose: bool = Options.verbose,
|
5096
5152
|
):
|
@@ -5098,13 +5154,40 @@ class CLIManager:
|
|
5098
5154
|
Display html dashboard with subnets list, stake, and neuron information.
|
5099
5155
|
"""
|
5100
5156
|
self.verbosity_handler(quiet, verbose)
|
5101
|
-
if is_linux():
|
5157
|
+
if use_wry and is_linux():
|
5102
5158
|
print_linux_dependency_message()
|
5103
|
-
|
5104
|
-
|
5105
|
-
|
5159
|
+
|
5160
|
+
if use_wry and save_file:
|
5161
|
+
print_error("Cannot save file when using PyWry.")
|
5162
|
+
raise typer.Exit()
|
5163
|
+
|
5164
|
+
if save_file:
|
5165
|
+
if not dashboard_path:
|
5166
|
+
dashboard_path = Prompt.ask(
|
5167
|
+
"Enter the [blue]path[/blue] where the dashboard HTML file will be saved",
|
5168
|
+
default=self.config.get("dashboard_path")
|
5169
|
+
or defaults.dashboard.path,
|
5170
|
+
)
|
5171
|
+
|
5172
|
+
if coldkey_ss58:
|
5173
|
+
if not is_valid_ss58_address(coldkey_ss58):
|
5174
|
+
print_error(f"Invalid SS58 address: {coldkey_ss58}")
|
5175
|
+
raise typer.Exit()
|
5176
|
+
wallet = None
|
5177
|
+
else:
|
5178
|
+
wallet = self.wallet_ask(
|
5179
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
5180
|
+
)
|
5181
|
+
|
5106
5182
|
return self._run_command(
|
5107
|
-
view.display_network_dashboard(
|
5183
|
+
view.display_network_dashboard(
|
5184
|
+
wallet=wallet,
|
5185
|
+
subtensor=self.initialize_chain(network),
|
5186
|
+
use_wry=use_wry,
|
5187
|
+
save_file=save_file,
|
5188
|
+
dashboard_path=dashboard_path,
|
5189
|
+
coldkey_ss58=coldkey_ss58,
|
5190
|
+
)
|
5108
5191
|
)
|
5109
5192
|
|
5110
5193
|
@staticmethod
|
bittensor_cli/src/__init__.py
CHANGED
@@ -1251,7 +1251,7 @@ class SubtensorInterface:
|
|
1251
1251
|
if _result is None:
|
1252
1252
|
return Balance(0).set_unit(netuid)
|
1253
1253
|
else:
|
1254
|
-
return Balance.from_rao(_result).set_unit(int(netuid))
|
1254
|
+
return Balance.from_rao(fixed_to_float(_result)).set_unit(int(netuid))
|
1255
1255
|
|
1256
1256
|
async def get_metagraph_info(
|
1257
1257
|
self, netuid: int, block_hash: Optional[str] = None
|
@@ -45,17 +45,33 @@ class _Hotkey:
|
|
45
45
|
self.ss58_address = hotkey_ss58
|
46
46
|
|
47
47
|
|
48
|
+
class _Coldkeypub:
|
49
|
+
def __init__(self, coldkey_ss58=None):
|
50
|
+
self.ss58_address = coldkey_ss58
|
51
|
+
|
52
|
+
|
48
53
|
class WalletLike:
|
49
|
-
def __init__(
|
54
|
+
def __init__(
|
55
|
+
self,
|
56
|
+
name=None,
|
57
|
+
hotkey_ss58=None,
|
58
|
+
hotkey_str=None,
|
59
|
+
coldkeypub_ss58=None,
|
60
|
+
):
|
50
61
|
self.name = name
|
51
62
|
self.hotkey_ss58 = hotkey_ss58
|
52
63
|
self.hotkey_str = hotkey_str
|
53
64
|
self._hotkey = _Hotkey(hotkey_ss58)
|
65
|
+
self._coldkeypub = _Coldkeypub(coldkeypub_ss58)
|
54
66
|
|
55
67
|
@property
|
56
68
|
def hotkey(self):
|
57
69
|
return self._hotkey
|
58
70
|
|
71
|
+
@property
|
72
|
+
def coldkeypub(self):
|
73
|
+
return self._coldkeypub
|
74
|
+
|
59
75
|
|
60
76
|
def print_console(message: str, colour: str, title: str, console: Console):
|
61
77
|
console.print(
|
@@ -1081,38 +1097,30 @@ def prompt_for_identity(
|
|
1081
1097
|
identity_fields = {}
|
1082
1098
|
|
1083
1099
|
fields = [
|
1084
|
-
("name", "[blue]Display name[/blue]", name),
|
1085
|
-
("url", "[blue]Web URL[/blue]", web_url),
|
1086
|
-
("image", "[blue]Image URL[/blue]", image_url),
|
1087
|
-
("discord", "[blue]Discord handle[/blue]", discord),
|
1088
|
-
("description", "[blue]Description[/blue]", description),
|
1089
|
-
("additional", "[blue]Additional information[/blue]", additional),
|
1090
|
-
("github_repo", "[blue]GitHub repository URL[/blue]", github_repo),
|
1100
|
+
("name", "[blue]Display name[/blue]", name, 256),
|
1101
|
+
("url", "[blue]Web URL[/blue]", web_url, 256),
|
1102
|
+
("image", "[blue]Image URL[/blue]", image_url, 1024),
|
1103
|
+
("discord", "[blue]Discord handle[/blue]", discord, 256),
|
1104
|
+
("description", "[blue]Description[/blue]", description, 1024),
|
1105
|
+
("additional", "[blue]Additional information[/blue]", additional, 1024),
|
1106
|
+
("github_repo", "[blue]GitHub repository URL[/blue]", github_repo, 256),
|
1091
1107
|
]
|
1092
1108
|
|
1093
|
-
text_rejection = partial(
|
1094
|
-
retry_prompt,
|
1095
|
-
rejection=lambda x: sys.getsizeof(x) > 113,
|
1096
|
-
rejection_text="[red]Error:[/red] Identity field must be <= 64 raw bytes.",
|
1097
|
-
)
|
1098
|
-
|
1099
1109
|
if not any(
|
1100
|
-
[
|
1101
|
-
name,
|
1102
|
-
web_url,
|
1103
|
-
image_url,
|
1104
|
-
discord,
|
1105
|
-
description,
|
1106
|
-
additional,
|
1107
|
-
github_repo,
|
1108
|
-
]
|
1110
|
+
[name, web_url, image_url, discord, description, additional, github_repo]
|
1109
1111
|
):
|
1110
1112
|
console.print(
|
1111
1113
|
"\n[yellow]All fields are optional. Press Enter to skip and keep the default/existing value.[/yellow]\n"
|
1112
1114
|
"[dark_sea_green3]Tip: Entering a space and pressing Enter will clear existing default value.\n"
|
1113
1115
|
)
|
1114
1116
|
|
1115
|
-
for key, prompt, value in fields:
|
1117
|
+
for key, prompt, value, byte_limit in fields:
|
1118
|
+
text_rejection = partial(
|
1119
|
+
retry_prompt,
|
1120
|
+
rejection=lambda x: len(x.encode("utf-8")) > byte_limit,
|
1121
|
+
rejection_text=f"[red]Error:[/red] {key} field must be <= {byte_limit} bytes.",
|
1122
|
+
)
|
1123
|
+
|
1116
1124
|
if value:
|
1117
1125
|
identity_fields[key] = value
|
1118
1126
|
else:
|
@@ -1154,50 +1162,51 @@ def prompt_for_subnet_identity(
|
|
1154
1162
|
"subnet_name",
|
1155
1163
|
"[blue]Subnet name [dim](optional)[/blue]",
|
1156
1164
|
subnet_name,
|
1157
|
-
lambda x: x and
|
1158
|
-
"[red]Error:[/red] Subnet name must be <=
|
1165
|
+
lambda x: x and len(x.encode("utf-8")) > 256,
|
1166
|
+
"[red]Error:[/red] Subnet name must be <= 256 bytes.",
|
1159
1167
|
),
|
1160
1168
|
(
|
1161
1169
|
"github_repo",
|
1162
1170
|
"[blue]GitHub repository URL [dim](optional)[/blue]",
|
1163
1171
|
github_repo,
|
1164
|
-
lambda x: x
|
1172
|
+
lambda x: x
|
1173
|
+
and (not is_valid_github_url(x) or len(x.encode("utf-8")) > 1024),
|
1165
1174
|
"[red]Error:[/red] Please enter a valid GitHub repository URL (e.g., https://github.com/username/repo).",
|
1166
1175
|
),
|
1167
1176
|
(
|
1168
1177
|
"subnet_contact",
|
1169
1178
|
"[blue]Contact email [dim](optional)[/blue]",
|
1170
1179
|
subnet_contact,
|
1171
|
-
lambda x: x and not is_valid_contact(x),
|
1180
|
+
lambda x: x and (not is_valid_contact(x) or len(x.encode("utf-8")) > 1024),
|
1172
1181
|
"[red]Error:[/red] Please enter a valid email address.",
|
1173
1182
|
),
|
1174
1183
|
(
|
1175
1184
|
"subnet_url",
|
1176
1185
|
"[blue]Subnet URL [dim](optional)[/blue]",
|
1177
1186
|
subnet_url,
|
1178
|
-
lambda x: x and
|
1179
|
-
"[red]Error:[/red] Please enter a valid URL.",
|
1187
|
+
lambda x: x and len(x.encode("utf-8")) > 1024,
|
1188
|
+
"[red]Error:[/red] Please enter a valid URL <= 1024 bytes.",
|
1180
1189
|
),
|
1181
1190
|
(
|
1182
1191
|
"discord",
|
1183
1192
|
"[blue]Discord handle [dim](optional)[/blue]",
|
1184
1193
|
discord,
|
1185
|
-
lambda x: x and
|
1186
|
-
"[red]Error:[/red] Please enter a valid Discord handle.",
|
1194
|
+
lambda x: x and len(x.encode("utf-8")) > 256,
|
1195
|
+
"[red]Error:[/red] Please enter a valid Discord handle <= 256 bytes.",
|
1187
1196
|
),
|
1188
1197
|
(
|
1189
1198
|
"description",
|
1190
1199
|
"[blue]Description [dim](optional)[/blue]",
|
1191
1200
|
description,
|
1192
|
-
lambda x: x and
|
1193
|
-
"[red]Error:[/red] Description must be <=
|
1201
|
+
lambda x: x and len(x.encode("utf-8")) > 1024,
|
1202
|
+
"[red]Error:[/red] Description must be <= 1024 bytes.",
|
1194
1203
|
),
|
1195
1204
|
(
|
1196
1205
|
"additional",
|
1197
1206
|
"[blue]Additional information [dim](optional)[/blue]",
|
1198
1207
|
additional,
|
1199
|
-
lambda x: x and
|
1200
|
-
"[red]Error:[/red] Additional information must be <=
|
1208
|
+
lambda x: x and len(x.encode("utf-8")) > 1024,
|
1209
|
+
"[red]Error:[/red] Additional information must be <= 1024 bytes.",
|
1201
1210
|
),
|
1202
1211
|
]
|
1203
1212
|
|
@@ -1257,16 +1266,18 @@ def is_valid_contact(contact: str) -> bool:
|
|
1257
1266
|
return bool(re.match(email_pattern, contact))
|
1258
1267
|
|
1259
1268
|
|
1260
|
-
def get_subnet_name(subnet_info) -> str:
|
1269
|
+
def get_subnet_name(subnet_info, max_length: int = 20) -> str:
|
1261
1270
|
"""Get the subnet name, prioritizing subnet_identity.subnet_name over subnet.subnet_name.
|
1271
|
+
Truncates the name if it exceeds max_length.
|
1262
1272
|
|
1263
1273
|
Args:
|
1264
|
-
|
1274
|
+
subnet_info: The subnet dynamic info
|
1275
|
+
max_length: Maximum length of the returned name. Names longer than this will be truncated with '...'
|
1265
1276
|
|
1266
1277
|
Returns:
|
1267
|
-
str: The subnet name or empty string if no name is found
|
1278
|
+
str: The subnet name (truncated if necessary) or empty string if no name is found
|
1268
1279
|
"""
|
1269
|
-
|
1280
|
+
name = (
|
1270
1281
|
subnet_info.subnet_identity.subnet_name
|
1271
1282
|
if hasattr(subnet_info, "subnet_identity")
|
1272
1283
|
and subnet_info.subnet_identity is not None
|
@@ -1274,6 +1285,10 @@ def get_subnet_name(subnet_info) -> str:
|
|
1274
1285
|
else (subnet_info.subnet_name if subnet_info.subnet_name is not None else "")
|
1275
1286
|
)
|
1276
1287
|
|
1288
|
+
if len(name) > max_length:
|
1289
|
+
return name[: max_length - 3] + "..."
|
1290
|
+
return name
|
1291
|
+
|
1277
1292
|
|
1278
1293
|
def print_linux_dependency_message():
|
1279
1294
|
"""Prints the WebKit dependency message for Linux systems."""
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
from functools import partial
|
3
3
|
|
4
|
-
import typer
|
5
4
|
from typing import TYPE_CHECKING, Optional
|
6
5
|
from rich.table import Table
|
7
6
|
from rich.prompt import Confirm, Prompt
|
@@ -20,7 +19,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
20
19
|
unlock_key,
|
21
20
|
)
|
22
21
|
from bittensor_wallet import Wallet
|
23
|
-
from bittensor_wallet.errors import KeyFileError
|
24
22
|
|
25
23
|
if TYPE_CHECKING:
|
26
24
|
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
@@ -338,7 +336,7 @@ async def stake_add(
|
|
338
336
|
|
339
337
|
if prompt:
|
340
338
|
if not Confirm.ask("Would you like to continue?"):
|
341
|
-
|
339
|
+
return False
|
342
340
|
if not unlock_key(wallet).success:
|
343
341
|
return False
|
344
342
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
|
3
3
|
from typing import TYPE_CHECKING, Optional
|
4
|
-
import typer
|
5
4
|
|
6
5
|
from bittensor_wallet import Wallet
|
7
6
|
from rich.prompt import Prompt
|
@@ -428,7 +427,7 @@ async def stake_list(
|
|
428
427
|
|
429
428
|
if not hotkeys_to_substakes:
|
430
429
|
print_error(f"No stakes found for coldkey ss58: ({coldkey_address})")
|
431
|
-
|
430
|
+
return
|
432
431
|
|
433
432
|
if live:
|
434
433
|
# Select one hotkey for live monitoring
|