bittensor-cli 8.2.0rc13__py3-none-any.whl → 8.2.0rc15__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 CHANGED
@@ -18,6 +18,6 @@
18
18
  from .cli import CLIManager
19
19
 
20
20
 
21
- __version__ = "8.2.0rc13"
21
+ __version__ = "8.2.0rc15"
22
22
 
23
23
  __all__ = ["CLIManager", "__version__"]
bittensor_cli/cli.py CHANGED
@@ -29,9 +29,10 @@ from bittensor_cli.src.bittensor.balances import Balance
29
29
  from bittensor_cli.src.bittensor.async_substrate_interface import (
30
30
  SubstrateRequestException,
31
31
  )
32
- from bittensor_cli.src.commands import subnets, sudo, wallets
32
+ from bittensor_cli.src.commands import sudo, wallets
33
33
  from bittensor_cli.src.commands import weights as weights_cmds
34
- from bittensor_cli.src.commands.stake import children_hotkeys, stake
34
+ from bittensor_cli.src.commands.subnets import price, subnets
35
+ from bittensor_cli.src.commands.stake import children_hotkeys, stake, move
35
36
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
36
37
  from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
37
38
  from bittensor_cli.src.bittensor.utils import (
@@ -41,13 +42,14 @@ from bittensor_cli.src.bittensor.utils import (
41
42
  is_valid_ss58_address,
42
43
  print_error,
43
44
  validate_chain_endpoint,
44
- retry_prompt,
45
45
  validate_netuid,
46
46
  is_rao_network,
47
47
  get_effective_network,
48
48
  prompt_for_identity,
49
49
  validate_uri,
50
50
  prompt_for_subnet_identity,
51
+ print_linux_dependency_message,
52
+ is_linux,
51
53
  )
52
54
  from typing_extensions import Annotated
53
55
  from textwrap import dedent
@@ -62,7 +64,7 @@ except ImportError:
62
64
  pass
63
65
 
64
66
 
65
- __version__ = "8.2.0rc13"
67
+ __version__ = "8.2.0rc15"
66
68
 
67
69
 
68
70
  _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
@@ -685,8 +687,14 @@ class CLIManager:
685
687
  "list", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
686
688
  )(self.stake_list)
687
689
  self.stake_app.command(
688
- "move", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
690
+ "move", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
689
691
  )(self.stake_move)
692
+ self.stake_app.command(
693
+ "transfer", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
694
+ )(self.stake_transfer)
695
+ self.stake_app.command(
696
+ "swap", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
697
+ )(self.stake_swap)
690
698
 
691
699
  # stake-children commands
692
700
  children_app = typer.Typer()
@@ -754,7 +762,7 @@ class CLIManager:
754
762
  "show", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
755
763
  )(self.subnets_show)
756
764
  self.subnets_app.command(
757
- "price", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"], hidden=True
765
+ "price", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
758
766
  )(self.subnets_price)
759
767
 
760
768
  # weights commands
@@ -2470,6 +2478,7 @@ class CLIManager:
2470
2478
  live: bool = Options.live,
2471
2479
  quiet: bool = Options.quiet,
2472
2480
  verbose: bool = Options.verbose,
2481
+ no_prompt: bool = Options.prompt,
2473
2482
  # TODO add: all-wallets, reuse_last, html_output
2474
2483
  ):
2475
2484
  """List all stake accounts for wallet."""
@@ -2498,7 +2507,7 @@ class CLIManager:
2498
2507
 
2499
2508
  return self._run_command(
2500
2509
  stake.stake_list(
2501
- wallet, coldkey_ss58, self.initialize_chain(network), live, verbose
2510
+ wallet, coldkey_ss58, self.initialize_chain(network), live, verbose, no_prompt
2502
2511
  )
2503
2512
  )
2504
2513
 
@@ -2926,14 +2935,18 @@ class CLIManager:
2926
2935
 
2927
2936
  def stake_move(
2928
2937
  self,
2929
- network=Options.network,
2938
+ network: Optional[list[str]] = Options.network,
2930
2939
  wallet_name=Options.wallet_name,
2931
2940
  wallet_path=Options.wallet_path,
2932
2941
  wallet_hotkey=Options.wallet_hotkey,
2933
- origin_netuid: int = typer.Option(help="Origin netuid", prompt=True),
2934
- destination_netuid: int = typer.Option(help="Destination netuid", prompt=True),
2942
+ origin_netuid: Optional[int] = typer.Option(
2943
+ None, "--origin-netuid", help="Origin netuid"
2944
+ ),
2945
+ destination_netuid: Optional[int] = typer.Option(
2946
+ None, "--dest-netuid", help="Destination netuid"
2947
+ ),
2935
2948
  destination_hotkey: Optional[str] = typer.Option(
2936
- None, help="Destination hotkey", prompt=False
2949
+ None, "--dest-ss58", "--dest", help="Destination hotkey", prompt=False
2937
2950
  ),
2938
2951
  amount: float = typer.Option(
2939
2952
  None,
@@ -2947,43 +2960,346 @@ class CLIManager:
2947
2960
  prompt: bool = Options.prompt,
2948
2961
  ):
2949
2962
  """
2950
- Move Staked TAO to a hotkey from one subnet to another.
2963
+ Move staked TAO between hotkeys while keeping the same coldkey ownership.
2964
+
2965
+ This command allows you to:
2966
+ - Move stake from one hotkey to another hotkey
2967
+ - Move stake between different subnets
2968
+ - Keep the same coldkey ownership
2969
+
2970
+ You can specify:
2971
+ - The origin subnet (--origin-netuid)
2972
+ - The destination subnet (--dest-netuid)
2973
+ - The destination hotkey (--dest-hotkey)
2974
+ - The amount to move (--amount)
2951
2975
 
2952
- THe move commands converts the origin subnet's dTao to Tao, and then converts Tao to destination subnet's dTao.
2976
+ If no arguments are provided, an interactive selection menu will be shown.
2953
2977
 
2954
2978
  EXAMPLE
2955
2979
 
2956
2980
  [green]$[/green] btcli stake move
2957
2981
  """
2958
- # TODO: Improve logic of moving stake (dest hotkey)
2959
- ask_for = (
2960
- [WO.NAME, WO.PATH] if destination_hotkey else [WO.NAME, WO.HOTKEY, WO.PATH]
2982
+ console.print(
2983
+ "[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
2961
2984
  )
2962
- validate = WV.WALLET if destination_hotkey else WV.WALLET_AND_HOTKEY
2985
+ if not destination_hotkey:
2986
+ dest_wallet_or_ss58 = Prompt.ask(
2987
+ "Enter the [blue]destination wallet[/blue] where destination hotkey is located or [blue]ss58 address[/blue]"
2988
+ )
2989
+ if is_valid_ss58_address(dest_wallet_or_ss58):
2990
+ destination_hotkey = dest_wallet_or_ss58
2991
+ else:
2992
+ dest_wallet = self.wallet_ask(
2993
+ dest_wallet_or_ss58,
2994
+ wallet_path,
2995
+ None,
2996
+ ask_for=[WO.NAME, WO.PATH],
2997
+ validate=WV.WALLET,
2998
+ )
2999
+ destination_hotkey = Prompt.ask(
3000
+ "Enter the [blue]destination hotkey[/blue] name",
3001
+ default=dest_wallet.hotkey_str,
3002
+ )
3003
+ destination_wallet = self.wallet_ask(
3004
+ dest_wallet_or_ss58,
3005
+ wallet_path,
3006
+ destination_hotkey,
3007
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3008
+ validate=WV.WALLET_AND_HOTKEY,
3009
+ )
3010
+ destination_hotkey = destination_wallet.hotkey.ss58_address
3011
+ else:
3012
+ if is_valid_ss58_address(destination_hotkey):
3013
+ destination_hotkey = destination_hotkey
3014
+ else:
3015
+ print_error(
3016
+ "Invalid destination hotkey ss58 address. Please enter a valid ss58 address or wallet name."
3017
+ )
3018
+ raise typer.Exit()
2963
3019
 
3020
+ if not wallet_name:
3021
+ wallet_name = Prompt.ask(
3022
+ "Enter the [blue]origin wallet name[/blue]",
3023
+ default=self.config.get("wallet_name") or defaults.wallet.name,
3024
+ )
2964
3025
  wallet = self.wallet_ask(
2965
- wallet_name,
2966
- wallet_path,
2967
- wallet_hotkey,
2968
- ask_for=ask_for,
2969
- validate=validate,
3026
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
2970
3027
  )
2971
- if not destination_hotkey:
2972
- destination_hotkey = wallet.hotkey.ss58_address
3028
+
3029
+ interactive_selection = False
3030
+ if not wallet_hotkey:
3031
+ origin_hotkey = Prompt.ask(
3032
+ "Enter the [blue]origin hotkey[/blue] name or "
3033
+ "[blue]ss58 address[/blue] where the stake will be moved from "
3034
+ "[dim](or Press Enter to view existing stakes)[/dim]"
3035
+ )
3036
+ if origin_hotkey == "":
3037
+ interactive_selection = True
3038
+
3039
+ elif is_valid_ss58_address(origin_hotkey):
3040
+ origin_hotkey = origin_hotkey
3041
+ else:
3042
+ wallet = self.wallet_ask(
3043
+ wallet_name,
3044
+ wallet_path,
3045
+ origin_hotkey,
3046
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3047
+ validate=WV.WALLET_AND_HOTKEY,
3048
+ )
3049
+ origin_hotkey = wallet.hotkey.ss58_address
3050
+ else:
3051
+ wallet = self.wallet_ask(
3052
+ wallet_name,
3053
+ wallet_path,
3054
+ wallet_hotkey,
3055
+ ask_for=[],
3056
+ validate=WV.WALLET_AND_HOTKEY,
3057
+ )
3058
+ origin_hotkey = wallet.hotkey.ss58_address
3059
+
3060
+ if not interactive_selection:
3061
+ if not origin_netuid:
3062
+ origin_netuid = IntPrompt.ask(
3063
+ "Enter the [blue]origin subnet[/blue] (netuid) to move stake from"
3064
+ )
3065
+
3066
+ if not destination_netuid:
3067
+ destination_netuid = IntPrompt.ask(
3068
+ "Enter the [blue]destination subnet[/blue] (netuid) to move stake to"
3069
+ )
2973
3070
 
2974
3071
  return self._run_command(
2975
- stake.move_stake(
3072
+ move.move_stake(
2976
3073
  subtensor=self.initialize_chain(network),
2977
3074
  wallet=wallet,
2978
3075
  origin_netuid=origin_netuid,
3076
+ origin_hotkey=origin_hotkey,
2979
3077
  destination_netuid=destination_netuid,
2980
3078
  destination_hotkey=destination_hotkey,
2981
3079
  amount=amount,
2982
3080
  stake_all=stake_all,
3081
+ interactive_selection=interactive_selection,
2983
3082
  prompt=prompt,
2984
3083
  )
2985
3084
  )
2986
3085
 
3086
+ def stake_transfer(
3087
+ self,
3088
+ network: Optional[list[str]] = Options.network,
3089
+ wallet_name: Optional[str] = Options.wallet_name,
3090
+ wallet_path: Optional[str] = Options.wallet_path,
3091
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3092
+ origin_netuid: Optional[int] = typer.Option(
3093
+ None,
3094
+ "--origin-netuid",
3095
+ help="The netuid to transfer stake from",
3096
+ ),
3097
+ dest_netuid: Optional[int] = typer.Option(
3098
+ None,
3099
+ "--dest-netuid",
3100
+ help="The netuid to transfer stake to",
3101
+ ),
3102
+ dest_ss58: Optional[str] = typer.Option(
3103
+ None,
3104
+ "--dest-ss58",
3105
+ "--dest",
3106
+ "--dest-coldkey",
3107
+ help="The destination wallet name or SS58 address to transfer stake to",
3108
+ ),
3109
+ amount: float = typer.Option(
3110
+ None,
3111
+ "--amount",
3112
+ "-a",
3113
+ help="Amount of stake to transfer",
3114
+ ),
3115
+ prompt: bool = Options.prompt,
3116
+ quiet: bool = Options.quiet,
3117
+ verbose: bool = Options.verbose,
3118
+ ):
3119
+ """
3120
+ Transfer stake between coldkeys while keeping the same hotkey ownership.
3121
+
3122
+ This command allows you to:
3123
+ - Transfer stake from one coldkey to another coldkey
3124
+ - Keep the same hotkey ownership
3125
+ - Transfer stake between different subnets
3126
+
3127
+ You can specify:
3128
+ - The origin subnet (--origin-netuid)
3129
+ - The destination subnet (--dest-netuid)
3130
+ - The destination wallet/address (--dest)
3131
+ - The amount to transfer (--amount)
3132
+
3133
+ If no arguments are provided, an interactive selection menu will be shown.
3134
+
3135
+ EXAMPLE
3136
+
3137
+ Transfer 100 TAO from subnet 1 to subnet 2:
3138
+ [green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest wallet2 --amount 100
3139
+
3140
+ Using SS58 address:
3141
+ [green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --amount 100
3142
+ """
3143
+ console.print(
3144
+ "[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
3145
+ )
3146
+ self.verbosity_handler(quiet, verbose)
3147
+
3148
+ wallet = self.wallet_ask(
3149
+ wallet_name,
3150
+ wallet_path,
3151
+ wallet_hotkey,
3152
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3153
+ validate=WV.WALLET_AND_HOTKEY,
3154
+ )
3155
+
3156
+ if not dest_ss58:
3157
+ dest_ss58 = Prompt.ask(
3158
+ "Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
3159
+ )
3160
+
3161
+ if is_valid_ss58_address(dest_ss58):
3162
+ dest_ss58 = dest_ss58
3163
+ else:
3164
+ dest_wallet = self.wallet_ask(
3165
+ dest_ss58,
3166
+ wallet_path,
3167
+ None,
3168
+ ask_for=[WO.NAME, WO.PATH],
3169
+ validate=WV.WALLET,
3170
+ )
3171
+ dest_ss58 = dest_wallet.coldkeypub.ss58_address
3172
+
3173
+ interactive_selection = False
3174
+ if origin_netuid is None and dest_netuid is None and not amount:
3175
+ interactive_selection = True
3176
+ else:
3177
+ if origin_netuid is None:
3178
+ origin_netuid = IntPrompt.ask(
3179
+ "Enter the [blue]origin subnet[/blue] (netuid)"
3180
+ )
3181
+ if not amount:
3182
+ amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to transfer")
3183
+
3184
+ if dest_netuid is None:
3185
+ dest_netuid = IntPrompt.ask(
3186
+ "Enter the [blue]destination subnet[/blue] (netuid)"
3187
+ )
3188
+
3189
+ return self._run_command(
3190
+ move.transfer_stake(
3191
+ wallet=wallet,
3192
+ subtensor=self.initialize_chain(network),
3193
+ origin_netuid=origin_netuid,
3194
+ dest_netuid=dest_netuid,
3195
+ dest_coldkey_ss58=dest_ss58,
3196
+ amount=amount,
3197
+ interactive_selection=interactive_selection,
3198
+ prompt=prompt,
3199
+ )
3200
+ )
3201
+
3202
+ def stake_swap(
3203
+ self,
3204
+ network: Optional[list[str]] = Options.network,
3205
+ wallet_name: Optional[str] = Options.wallet_name,
3206
+ wallet_path: Optional[str] = Options.wallet_path,
3207
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3208
+ origin_netuid: Optional[int] = typer.Option(
3209
+ None,
3210
+ "--origin-netuid",
3211
+ "-o",
3212
+ "--origin",
3213
+ help="The netuid to swap stake from",
3214
+ ),
3215
+ dest_netuid: Optional[int] = typer.Option(
3216
+ None,
3217
+ "--dest-netuid",
3218
+ "-d",
3219
+ "--dest",
3220
+ help="The netuid to swap stake to",
3221
+ ),
3222
+ amount: float = typer.Option(
3223
+ None,
3224
+ "--amount",
3225
+ "-a",
3226
+ help="Amount of stake to swap",
3227
+ ),
3228
+ swap_all: bool = typer.Option(
3229
+ False,
3230
+ "--swap-all",
3231
+ "--all",
3232
+ help="Swap all available stake",
3233
+ ),
3234
+ prompt: bool = Options.prompt,
3235
+ wait_for_inclusion: bool = Options.wait_for_inclusion,
3236
+ wait_for_finalization: bool = Options.wait_for_finalization,
3237
+ quiet: bool = Options.quiet,
3238
+ verbose: bool = Options.verbose,
3239
+ ):
3240
+ """
3241
+ Swap stake between different subnets while keeping the same coldkey-hotkey pair ownership.
3242
+
3243
+ This command allows you to:
3244
+ - Move stake from one subnet to another subnet
3245
+ - Keep the same coldkey ownership
3246
+ - Keep the same hotkey ownership
3247
+
3248
+ You can specify:
3249
+ - The origin subnet (--origin-netuid)
3250
+ - The destination subnet (--dest-netuid)
3251
+ - The amount to swap (--amount)
3252
+
3253
+ If no arguments are provided, an interactive selection menu will be shown.
3254
+
3255
+ EXAMPLE
3256
+
3257
+ Swap 100 TAO from subnet 1 to subnet 2:
3258
+ [green]$[/green] btcli stake swap --wallet-name default --wallet-hotkey default --origin-netuid 1 --dest-netuid 2 --amount 100
3259
+ """
3260
+ console.print(
3261
+ "[dim]This command moves stake from one subnet to another subnet while keeping the same coldkey-hotkey pair.[/dim]"
3262
+ )
3263
+ self.verbosity_handler(quiet, verbose)
3264
+
3265
+ wallet = self.wallet_ask(
3266
+ wallet_name,
3267
+ wallet_path,
3268
+ wallet_hotkey,
3269
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3270
+ validate=WV.WALLET_AND_HOTKEY,
3271
+ )
3272
+
3273
+ interactive_selection = False
3274
+ if origin_netuid is None and dest_netuid is None and not amount:
3275
+ interactive_selection = True
3276
+ else:
3277
+ if origin_netuid is None:
3278
+ origin_netuid = IntPrompt.ask(
3279
+ "Enter the [blue]origin subnet[/blue] (netuid)"
3280
+ )
3281
+ if dest_netuid is None:
3282
+ dest_netuid = IntPrompt.ask(
3283
+ "Enter the [blue]destination subnet[/blue] (netuid)"
3284
+ )
3285
+ if not amount and not swap_all:
3286
+ amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
3287
+
3288
+ return self._run_command(
3289
+ move.swap_stake(
3290
+ wallet=wallet,
3291
+ subtensor=self.initialize_chain(network),
3292
+ origin_netuid=origin_netuid,
3293
+ destination_netuid=dest_netuid,
3294
+ amount=amount,
3295
+ swap_all=swap_all,
3296
+ interactive_selection=interactive_selection,
3297
+ prompt=prompt,
3298
+ wait_for_inclusion=wait_for_inclusion,
3299
+ wait_for_finalization=wait_for_finalization,
3300
+ )
3301
+ )
3302
+
2987
3303
  def stake_get_children(
2988
3304
  self,
2989
3305
  wallet_name: Optional[str] = Options.wallet_name,
@@ -3561,23 +3877,88 @@ class CLIManager:
3561
3877
  def subnets_price(
3562
3878
  self,
3563
3879
  network: Optional[list[str]] = Options.network,
3564
- netuid: int = Options.netuid,
3880
+ netuids: str = typer.Option(
3881
+ None,
3882
+ "--netuids",
3883
+ "--netuid",
3884
+ "-n",
3885
+ help="Netuid(s) to show the price for.",
3886
+ ),
3565
3887
  interval_hours: int = typer.Option(
3566
3888
  24,
3567
3889
  "--interval-hours",
3568
3890
  "--interval",
3569
3891
  help="The number of hours to show the historical price for.",
3570
3892
  ),
3893
+ all_netuids: bool = typer.Option(
3894
+ False,
3895
+ "--all-netuids",
3896
+ "--all",
3897
+ help="Show the price for all subnets.",
3898
+ ),
3899
+ log_scale: bool = typer.Option(
3900
+ False,
3901
+ "--log-scale",
3902
+ "--log",
3903
+ help="Show the price in log scale.",
3904
+ ),
3905
+ html_output: bool = Options.html_output,
3571
3906
  ):
3572
3907
  """
3573
3908
  Shows the historical price of a subnet for the past 24 hours.
3574
3909
 
3910
+ This command displays the historical price of a subnet for the past 24 hours.
3911
+ If the `--all` flag is used, the command will display the price for all subnets in html format.
3912
+ If the `--html` flag is used, the command will display the price in an HTML chart.
3913
+ If the `--log-scale` flag is used, the command will display the price in log scale.
3914
+ If no html flag is used, the command will display the price in the cli.
3915
+
3575
3916
  EXAMPLE
3576
3917
 
3577
3918
  [green]$[/green] btcli subnets price --netuid 1
3919
+ [green]$[/green] btcli subnets price --netuid 1 --html --log
3920
+ [green]$[/green] btcli subnets price --all --html
3921
+ [green]$[/green] btcli subnets price --netuids 1,2,3,4 --html
3578
3922
  """
3923
+ if netuids:
3924
+ netuids = parse_to_list(
3925
+ netuids,
3926
+ int,
3927
+ "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
3928
+ )
3929
+ if all_netuids and netuids:
3930
+ print_error("Cannot specify both --netuid and --all-netuids")
3931
+ raise typer.Exit()
3932
+
3933
+ if not netuids and not all_netuids:
3934
+ netuids = Prompt.ask(
3935
+ "Enter the [blue]netuid(s)[/blue] to view the price of in comma-separated format [dim](or Press Enter to view all subnets)[/dim]",
3936
+ )
3937
+ if not netuids:
3938
+ all_netuids = True
3939
+ html_output = True
3940
+ else:
3941
+ netuids = parse_to_list(
3942
+ netuids,
3943
+ int,
3944
+ "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
3945
+ )
3946
+
3947
+ if all_netuids:
3948
+ html_output = True
3949
+
3950
+ if html_output and is_linux():
3951
+ print_linux_dependency_message()
3952
+
3579
3953
  return self._run_command(
3580
- subnets.price(self.initialize_chain(network), netuid, interval_hours)
3954
+ price.price(
3955
+ self.initialize_chain(network),
3956
+ netuids,
3957
+ all_netuids,
3958
+ interval_hours,
3959
+ html_output,
3960
+ log_scale,
3961
+ )
3581
3962
  )
3582
3963
 
3583
3964
  def subnets_show(
@@ -3661,8 +4042,10 @@ class CLIManager:
3661
4042
  wallet_hotkey,
3662
4043
  ask_for=[
3663
4044
  WO.NAME,
4045
+ WO.HOTKEY,
4046
+ WO.PATH,
3664
4047
  ],
3665
- validate=WV.WALLET,
4048
+ validate=WV.WALLET_AND_HOTKEY,
3666
4049
  )
3667
4050
  identity = prompt_for_subnet_identity(
3668
4051
  subnet_name=subnet_name,
@@ -869,6 +869,7 @@ HELP_PANELS = {
869
869
  "STAKE": {
870
870
  "STAKE_MGMT": "Stake Management",
871
871
  "CHILD": "Child Hotkeys",
872
+ "MOVEMENT": "Stake Movement",
872
873
  },
873
874
  "SUDO": {
874
875
  "CONFIG": "Subnet Configuration",
@@ -17,7 +17,7 @@
17
17
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
18
  # DEALINGS IN THE SOFTWARE.
19
19
 
20
- from typing import Union
20
+ from typing import Union, TypedDict
21
21
  from bittensor_cli.src import UNITS
22
22
 
23
23
 
@@ -301,3 +301,31 @@ class Balance:
301
301
  self.unit = Balance.get_unit(netuid)
302
302
  self.rao_unit = Balance.get_unit(netuid)
303
303
  return self
304
+
305
+
306
+ class FixedPoint(TypedDict):
307
+ """
308
+ Represents a fixed point ``U64F64`` number.
309
+ Where ``bits`` is a U128 representation of the fixed point number.
310
+
311
+ This matches the type of the Alpha shares.
312
+ """
313
+
314
+ bits: int
315
+
316
+
317
+ def fixed_to_float(fixed: FixedPoint) -> float:
318
+ # Currently this is stored as a U64F64
319
+ # which is 64 bits of integer and 64 bits of fractional
320
+ uint_bits = 64
321
+ frac_bits = 64
322
+
323
+ data: int = fixed["bits"]
324
+
325
+ # Shift bits to extract integer part (assuming 64 bits for integer part)
326
+ integer_part = data >> frac_bits
327
+ fractional_part = data & (2**frac_bits - 1)
328
+
329
+ frac_float = fractional_part / (2**frac_bits)
330
+
331
+ return integer_part + frac_float
@@ -900,6 +900,7 @@ class DynamicInfo:
900
900
  pending_root_emission: Balance
901
901
  network_registered_at: int
902
902
  subnet_identity: Optional[SubnetIdentity]
903
+ subnet_volume: float
903
904
 
904
905
  @classmethod
905
906
  def from_vec_u8(cls, vec_u8: list[int]) -> Optional["DynamicInfo"]:
@@ -938,6 +939,7 @@ class DynamicInfo:
938
939
  alpha_in = Balance.from_rao(decoded["alpha_in"]).set_unit(netuid)
939
940
  alpha_out = Balance.from_rao(decoded["alpha_out"]).set_unit(netuid)
940
941
  tao_in = Balance.from_rao(decoded["tao_in"]).set_unit(0)
942
+ subnet_volume = Balance.from_rao(decoded["subnet_volume"]).set_unit(netuid)
941
943
  alpha_out_emission = Balance.from_rao(decoded["alpha_out_emission"]).set_unit(
942
944
  netuid
943
945
  )
@@ -991,6 +993,7 @@ class DynamicInfo:
991
993
  pending_root_emission=pending_root_emission,
992
994
  network_registered_at=int(decoded["network_registered_at"]),
993
995
  subnet_identity=subnet_identity,
996
+ subnet_volume=subnet_volume,
994
997
  )
995
998
 
996
999
  def tao_to_alpha(self, tao: Balance) -> Balance:
@@ -1035,7 +1038,13 @@ class DynamicInfo:
1035
1038
  else:
1036
1039
  alpha_returned = tao.set_unit(self.netuid)
1037
1040
  slippage = Balance.from_tao(0)
1038
- return alpha_returned, slippage
1041
+
1042
+ slippage_pct_float = (
1043
+ 100 * float(slippage) / float(slippage + alpha_returned)
1044
+ if slippage + alpha_returned != 0
1045
+ else 0
1046
+ )
1047
+ return alpha_returned, slippage, slippage_pct_float
1039
1048
 
1040
1049
  def alpha_to_tao_with_slippage(self, alpha: Balance) -> tuple[Balance, Balance]:
1041
1050
  """
@@ -1063,7 +1072,12 @@ class DynamicInfo:
1063
1072
  else:
1064
1073
  tao_returned = alpha.set_unit(0)
1065
1074
  slippage = Balance.from_tao(0)
1066
- return tao_returned, slippage
1075
+ slippage_pct_float = (
1076
+ 100 * float(slippage) / float(slippage + tao_returned)
1077
+ if slippage + tao_returned != 0
1078
+ else 0
1079
+ )
1080
+ return tao_returned, slippage, slippage_pct_float
1067
1081
 
1068
1082
 
1069
1083
  @dataclass
@@ -1590,6 +1604,7 @@ custom_rpc_type_registry = {
1590
1604
  ["pending_alpha_emission", "Compact<u64>"],
1591
1605
  ["pending_root_emission", "Compact<u64>"],
1592
1606
  ["network_registered_at", "Compact<u64>"],
1607
+ ["subnet_volume", "Compact<u128>"],
1593
1608
  ["subnet_identity", "Option<SubnetIdentity>"],
1594
1609
  ],
1595
1610
  },