11#!/usr/bin/env python3
22import asyncio
33import curses
4+ import copy
45import importlib
56import json
67import os .path
1011import traceback
1112import warnings
1213from pathlib import Path
13- from typing import Coroutine , Optional
14+ from typing import Coroutine , Optional , Union
1415from dataclasses import fields
1516
1617import rich
@@ -89,6 +90,23 @@ class Options:
8990 Re-usable typer args
9091 """
9192
93+ @classmethod
94+ def edit_help (cls , option_name : str , help_text : str ):
95+ """
96+ Edits the `help` attribute of a copied given Typer option in this class, returning
97+ the modified Typer option.
98+
99+ Args:
100+ option_name: the name of the option (e.g. "wallet_name")
101+ help_text: New help text to be used (e.g. "Wallet's name")
102+
103+ Returns:
104+ Modified Typer Option with new help text.
105+ """
106+ copied_attr = copy .copy (getattr (cls , option_name ))
107+ setattr (copied_attr , "help" , help_text )
108+ return copied_attr
109+
92110 wallet_name = typer .Option (
93111 None ,
94112 "--wallet-name" ,
@@ -3202,7 +3220,11 @@ def stake_add(
32023220 help = "When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
32033221 "hotkeys in `--include-hotkeys`." ,
32043222 ),
3205- netuid : Optional [int ] = Options .netuid_not_req ,
3223+ netuids : Optional [str ] = Options .edit_help (
3224+ "netuids" ,
3225+ "Netuid(s) to for which to add stake. Specify multiple netuids by separating with a comma, e.g."
3226+ "`btcli st add -n 1,2,3" ,
3227+ ),
32063228 all_netuids : bool = Options .all_netuids ,
32073229 wallet_name : str = Options .wallet_name ,
32083230 wallet_path : str = Options .wallet_path ,
@@ -3242,52 +3264,76 @@ def stake_add(
32423264 6. Stake all balance to a subnet:
32433265 [green]$[/green] btcli stake add --all --netuid 3
32443266
3267+ 7. Stake the same amount to multiple subnets:
3268+ [green]$[/green] btcli stake add --amount 100 --netuids 4,5,6
3269+
32453270 [bold]Safe Staking Parameters:[/bold]
32463271 • [blue]--safe[/blue]: Enables rate tolerance checks
32473272 • [blue]--tolerance[/blue]: Maximum % rate change allowed (0.05 = 5%)
32483273 • [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
32493274
32503275 """
3276+ netuids = netuids or []
32513277 self .verbosity_handler (quiet , verbose , json_output )
32523278 safe_staking = self .ask_safe_staking (safe_staking )
32533279 if safe_staking :
32543280 rate_tolerance = self .ask_rate_tolerance (rate_tolerance )
32553281 allow_partial_stake = self .ask_partial_stake (allow_partial_stake )
32563282 console .print ("\n " )
3257- netuid = get_optional_netuid (netuid , all_netuids )
3283+
3284+ if netuids :
3285+ netuids = parse_to_list (
3286+ netuids , int , "Netuids must be ints separated by commas" , False
3287+ )
3288+ else :
3289+ netuid_ = get_optional_netuid (None , all_netuids )
3290+ netuids = [netuid_ ] if netuid_ else None
3291+ if netuids :
3292+ for netuid_ in netuids :
3293+ # ensure no negative netuids make it into our list
3294+ validate_netuid (netuid_ )
32583295
32593296 if stake_all and amount :
32603297 print_error (
32613298 "Cannot specify an amount and 'stake-all'. Choose one or the other."
32623299 )
3263- raise typer . Exit ()
3300+ return
32643301
32653302 if stake_all and not amount :
32663303 if not Confirm .ask ("Stake all the available TAO tokens?" , default = False ):
3267- raise typer .Exit ()
3304+ return
3305+
3306+ if (
3307+ stake_all
3308+ and (isinstance (netuids , list ) and len (netuids ) > 1 )
3309+ or (netuids is None )
3310+ ):
3311+ print_error ("Cannot stake all to multiple subnets." )
3312+ return
32683313
32693314 if all_hotkeys and include_hotkeys :
32703315 print_error (
32713316 "You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
32723317 "should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
32733318 )
3274- raise typer . Exit ()
3319+ return
32753320
32763321 if include_hotkeys and exclude_hotkeys :
32773322 print_error (
32783323 "You have specified options for both including and excluding hotkeys. Select one or the other."
32793324 )
3280- raise typer . Exit ()
3325+ return
32813326
32823327 if not wallet_hotkey and not all_hotkeys and not include_hotkeys :
32833328 if not wallet_name :
32843329 wallet_name = Prompt .ask (
32853330 "Enter the [blue]wallet name[/blue]" ,
32863331 default = self .config .get ("wallet_name" ) or defaults .wallet .name ,
32873332 )
3288- if netuid is not None :
3333+ if netuids is not None :
32893334 hotkey_or_ss58 = Prompt .ask (
3290- "Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim](or Press Enter to view delegates)[/dim]" ,
3335+ "Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim]"
3336+ "(or Press Enter to view delegates)[/dim]" ,
32913337 )
32923338 else :
32933339 hotkey_or_ss58 = Prompt .ask (
@@ -3299,10 +3345,18 @@ def stake_add(
32993345 wallet = self .wallet_ask (
33003346 wallet_name , wallet_path , wallet_hotkey , ask_for = [WO .NAME , WO .PATH ]
33013347 )
3348+ if len (netuids ) > 1 :
3349+ netuid_ = IntPrompt .ask (
3350+ "Enter the netuid for which to show delegates" ,
3351+ choices = [str (x ) for x in netuids ],
3352+ )
3353+ else :
3354+ netuid_ = netuids [0 ]
3355+
33023356 selected_hotkey = self ._run_command (
33033357 subnets .show (
33043358 subtensor = self .initialize_chain (network ),
3305- netuid = netuid ,
3359+ netuid = netuid_ ,
33063360 sort = False ,
33073361 max_rows = 12 ,
33083362 prompt = False ,
@@ -3312,7 +3366,7 @@ def stake_add(
33123366 )
33133367 if not selected_hotkey :
33143368 print_error ("No delegate selected. Exiting." )
3315- raise typer . Exit ()
3369+ return
33163370 include_hotkeys = selected_hotkey
33173371 elif is_valid_ss58_address (hotkey_or_ss58 ):
33183372 wallet = self .wallet_ask (
@@ -3373,8 +3427,8 @@ def stake_add(
33733427 )
33743428 if free_balance == Balance .from_tao (0 ):
33753429 print_error ("You dont have any balance to stake." )
3376- raise typer . Exit ()
3377- if netuid is not None :
3430+ return
3431+ if netuids :
33783432 amount = FloatPrompt .ask (
33793433 f"Amount to [{ COLORS .G .SUBHEAD_MAIN } ]stake (TAO τ)"
33803434 )
@@ -3396,7 +3450,7 @@ def stake_add(
33963450 add_stake .stake_add (
33973451 wallet ,
33983452 self .initialize_chain (network ),
3399- netuid ,
3453+ netuids ,
34003454 stake_all ,
34013455 amount ,
34023456 prompt ,
@@ -4796,12 +4850,9 @@ def subnets_list(
47964850 def subnets_price (
47974851 self ,
47984852 network : Optional [list [str ]] = Options .network ,
4799- netuids : str = typer .Option (
4800- None ,
4801- "--netuids" ,
4802- "--netuid" ,
4803- "-n" ,
4804- help = "Netuid(s) to show the price for." ,
4853+ netuids : str = Options .edit_help (
4854+ "netuids" ,
4855+ "Netuids to show the price for. Separate multiple netuids with a comma, for example: `-n 0,1,2`." ,
48054856 ),
48064857 interval_hours : int = typer .Option (
48074858 24 ,
0 commit comments