bittensor-cli 9.0.1__py3-none-any.whl → 9.0.3__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
@@ -16,8 +16,7 @@
16
16
  # DEALINGS IN THE SOFTWARE.
17
17
 
18
18
  from .cli import CLIManager
19
+ from .version import __version__, __version_as_int__
19
20
 
20
21
 
21
- __version__ = "9.0.1"
22
-
23
- __all__ = ["CLIManager", "__version__"]
22
+ __all__ = ["CLIManager"]
bittensor_cli/cli.py CHANGED
@@ -26,7 +26,9 @@ from bittensor_cli.src import (
26
26
  WalletValidationTypes as WV,
27
27
  Constants,
28
28
  COLOR_PALETTE,
29
+ HYPERPARAMS,
29
30
  )
31
+ from bittensor_cli.version import __version__, __version_as_int__
30
32
  from bittensor_cli.src.bittensor import utils
31
33
  from bittensor_cli.src.bittensor.balances import Balance
32
34
  from async_substrate_interface.errors import SubstrateRequestException
@@ -72,21 +74,6 @@ except ImportError:
72
74
  pass
73
75
 
74
76
 
75
- __version__ = "9.0.1"
76
-
77
-
78
- _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
79
- _version_split = _core_version.split(".")
80
- __version_info__ = tuple(int(part) for part in _version_split)
81
- _version_int_base = 1000
82
- assert max(__version_info__) < _version_int_base
83
-
84
- __version_as_int__: int = sum(
85
- e * (_version_int_base**i) for i, e in enumerate(reversed(__version_info__))
86
- )
87
- assert __version_as_int__ < 2**31 # fits in int32
88
- __new_signature_version__ = 360
89
-
90
77
  _epilog = "Made with [bold red]:heart:[/bold red] by The Openτensor Foundaτion"
91
78
 
92
79
  np.set_printoptions(precision=8, suppress=True, floatmode="fixed")
@@ -124,7 +111,7 @@ class Options:
124
111
  )
125
112
  mnemonic = typer.Option(
126
113
  None,
127
- help="Mnemonic used to regenerate your key. For example: horse cart dog ...",
114
+ help='Mnemonic used to regenerate your key. For example: "horse cart dog ..."',
128
115
  )
129
116
  seed = typer.Option(
130
117
  None, help="Seed hex string used to regenerate your key. For example: 0x1234..."
@@ -492,7 +479,7 @@ def version_callback(value: bool):
492
479
  f"{repo.active_branch.name}/"
493
480
  f"{repo.commit()}"
494
481
  )
495
- except (NameError, GitError):
482
+ except (TypeError, GitError):
496
483
  version = f"BTCLI version: {__version__}"
497
484
  typer.echo(version)
498
485
  raise typer.Exit()
@@ -2130,6 +2117,7 @@ class CLIManager:
2130
2117
  # Example usage:
2131
2118
 
2132
2119
  [green]$[/green] btcli wallet regen_hotkey --seed 0x1234...
2120
+ [green]$[/green] btcli wallet regen-hotkey --mnemonic "word1 word2 ... word12"
2133
2121
 
2134
2122
  [bold]Note[/bold]: This command is essential for users who need to regenerate their hotkey, possibly for security upgrades or key recovery.
2135
2123
  It should be used with caution to avoid accidental overwriting of existing keys.
@@ -3221,11 +3209,23 @@ class CLIManager:
3221
3209
  else:
3222
3210
  print_error("Invalid hotkey ss58 address.")
3223
3211
  raise typer.Exit()
3224
- else:
3225
- hotkey_or_ss58 = Prompt.ask(
3226
- "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from",
3227
- default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
3212
+ elif all_hotkeys:
3213
+ wallet = self.wallet_ask(
3214
+ wallet_name,
3215
+ wallet_path,
3216
+ wallet_hotkey,
3217
+ ask_for=[WO.NAME, WO.PATH],
3228
3218
  )
3219
+ else:
3220
+ if not hotkey_ss58_address and not wallet_hotkey:
3221
+ hotkey_or_ss58 = Prompt.ask(
3222
+ "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from [dim](or enter 'all' to unstake from all hotkeys)[/dim]",
3223
+ default=self.config.get("wallet_hotkey")
3224
+ or defaults.wallet.hotkey,
3225
+ )
3226
+ else:
3227
+ hotkey_or_ss58 = hotkey_ss58_address or wallet_hotkey
3228
+
3229
3229
  if is_valid_ss58_address(hotkey_or_ss58):
3230
3230
  hotkey_ss58_address = hotkey_or_ss58
3231
3231
  wallet = self.wallet_ask(
@@ -3234,6 +3234,14 @@ class CLIManager:
3234
3234
  wallet_hotkey,
3235
3235
  ask_for=[WO.NAME, WO.PATH],
3236
3236
  )
3237
+ elif hotkey_or_ss58 == "all":
3238
+ all_hotkeys = True
3239
+ wallet = self.wallet_ask(
3240
+ wallet_name,
3241
+ wallet_path,
3242
+ wallet_hotkey,
3243
+ ask_for=[WO.NAME, WO.PATH],
3244
+ )
3237
3245
  else:
3238
3246
  wallet_hotkey = hotkey_or_ss58
3239
3247
  wallet = self.wallet_ask(
@@ -3249,6 +3257,9 @@ class CLIManager:
3249
3257
  subtensor=self.initialize_chain(network),
3250
3258
  hotkey_ss58_address=hotkey_ss58_address,
3251
3259
  unstake_all_alpha=unstake_all_alpha,
3260
+ all_hotkeys=all_hotkeys,
3261
+ include_hotkeys=include_hotkeys,
3262
+ exclude_hotkeys=exclude_hotkeys,
3252
3263
  prompt=prompt,
3253
3264
  )
3254
3265
  )
@@ -3310,9 +3321,9 @@ class CLIManager:
3310
3321
  def stake_move(
3311
3322
  self,
3312
3323
  network: Optional[list[str]] = Options.network,
3313
- wallet_name=Options.wallet_name,
3314
- wallet_path=Options.wallet_path,
3315
- wallet_hotkey=Options.wallet_hotkey,
3324
+ wallet_name: Optional[str] = Options.wallet_name,
3325
+ wallet_path: Optional[str] = Options.wallet_path,
3326
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3316
3327
  origin_netuid: Optional[int] = typer.Option(
3317
3328
  None, "--origin-netuid", help="Origin netuid"
3318
3329
  ),
@@ -3522,14 +3533,45 @@ class CLIManager:
3522
3533
  )
3523
3534
  self.verbosity_handler(quiet, verbose)
3524
3535
 
3536
+ if not wallet_name:
3537
+ wallet_name = Prompt.ask(
3538
+ "Enter the [blue]origin wallet name[/blue]",
3539
+ default=self.config.get("wallet_name") or defaults.wallet.name,
3540
+ )
3525
3541
  wallet = self.wallet_ask(
3526
- wallet_name,
3527
- wallet_path,
3528
- wallet_hotkey,
3529
- ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3530
- validate=WV.WALLET_AND_HOTKEY,
3542
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
3531
3543
  )
3532
3544
 
3545
+ if not wallet_hotkey:
3546
+ origin_hotkey = Prompt.ask(
3547
+ "Enter the [blue]origin hotkey[/blue] name or "
3548
+ "[blue]ss58 address[/blue] where the stake will be moved from",
3549
+ default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
3550
+ )
3551
+ if is_valid_ss58_address(origin_hotkey):
3552
+ origin_hotkey = origin_hotkey
3553
+ else:
3554
+ wallet = self.wallet_ask(
3555
+ wallet_name,
3556
+ wallet_path,
3557
+ origin_hotkey,
3558
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3559
+ validate=WV.WALLET_AND_HOTKEY,
3560
+ )
3561
+ origin_hotkey = wallet.hotkey.ss58_address
3562
+ else:
3563
+ if is_valid_ss58_address(wallet_hotkey):
3564
+ origin_hotkey = wallet_hotkey
3565
+ else:
3566
+ wallet = self.wallet_ask(
3567
+ wallet_name,
3568
+ wallet_path,
3569
+ wallet_hotkey,
3570
+ ask_for=[],
3571
+ validate=WV.WALLET_AND_HOTKEY,
3572
+ )
3573
+ origin_hotkey = wallet.hotkey.ss58_address
3574
+
3533
3575
  if not dest_ss58:
3534
3576
  dest_ss58 = Prompt.ask(
3535
3577
  "Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
@@ -3567,6 +3609,7 @@ class CLIManager:
3567
3609
  move_stake.transfer_stake(
3568
3610
  wallet=wallet,
3569
3611
  subtensor=self.initialize_chain(network),
3612
+ origin_hotkey=origin_hotkey,
3570
3613
  origin_netuid=origin_netuid,
3571
3614
  dest_netuid=dest_netuid,
3572
3615
  dest_coldkey_ss58=dest_ss58,
@@ -3961,7 +4004,7 @@ class CLIManager:
3961
4004
  param_name: str = typer.Option(
3962
4005
  "", "--param", "--parameter", help="The subnet hyperparameter to set"
3963
4006
  ),
3964
- param_value: str = typer.Option(
4007
+ param_value: Optional[str] = typer.Option(
3965
4008
  "", "--value", help="Value to set the hyperparameter to."
3966
4009
  ),
3967
4010
  quiet: bool = Options.quiet,
@@ -4010,9 +4053,12 @@ class CLIManager:
4010
4053
  param_value = f"{low_val},{high_val}"
4011
4054
 
4012
4055
  if not param_value:
4013
- param_value = Prompt.ask(
4014
- f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format"
4015
- )
4056
+ if HYPERPARAMS.get(param_name):
4057
+ param_value = Prompt.ask(
4058
+ f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format"
4059
+ )
4060
+ else:
4061
+ param_value = None
4016
4062
 
4017
4063
  wallet = self.wallet_ask(
4018
4064
  wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
@@ -672,7 +672,7 @@ HYPERPARAMS = {
672
672
  "commit_reveal_weights_enabled": ("sudo_set_commit_reveal_weights_enabled", False),
673
673
  "alpha_values": ("sudo_set_alpha_values", False),
674
674
  "liquid_alpha_enabled": ("sudo_set_liquid_alpha_enabled", False),
675
- "network_registration_allowed": ("sudo_set_network_registration_allowed", False),
675
+ "registration_allowed": ("sudo_set_network_registration_allowed", False),
676
676
  "network_pow_registration_allowed": (
677
677
  "sudo_set_network_pow_registration_allowed",
678
678
  False,
@@ -76,7 +76,7 @@ class Balance:
76
76
  if self.unit == UNITS[0]:
77
77
  return f"{self.unit} {float(self.tao):,.4f}"
78
78
  else:
79
- return f"{float(self.tao):,.4f} {self.unit}\u200e"
79
+ return f"\u200e{float(self.tao):,.4f} {self.unit}\u200e"
80
80
 
81
81
  def __rich__(self):
82
82
  return "[green]{}[/green][green]{}[/green][green].[/green][dim green]{}[/dim green]".format(
@@ -156,7 +156,7 @@ class SubnetHyperparameters(InfoBase):
156
156
  def _fix_decoded(
157
157
  cls, decoded: Union[dict, "SubnetHyperparameters"]
158
158
  ) -> "SubnetHyperparameters":
159
- return SubnetHyperparameters(
159
+ return cls(
160
160
  rho=decoded.get("rho"),
161
161
  kappa=decoded.get("kappa"),
162
162
  immunity_period=decoded.get("immunity_period"),
@@ -197,6 +197,7 @@ class StakeInfo(InfoBase):
197
197
  stake: Balance # Stake for the hotkey-coldkey pair
198
198
  locked: Balance # Stake which is locked.
199
199
  emission: Balance # Emission for the hotkey-coldkey pair
200
+ tao_emission: Balance # TAO emission for the hotkey-coldkey pair
200
201
  drain: int
201
202
  is_registered: bool
202
203
 
@@ -208,11 +209,20 @@ class StakeInfo(InfoBase):
208
209
  stake = Balance.from_rao(decoded.get("stake")).set_unit(netuid)
209
210
  locked = Balance.from_rao(decoded.get("locked")).set_unit(netuid)
210
211
  emission = Balance.from_rao(decoded.get("emission")).set_unit(netuid)
212
+ tao_emission = Balance.from_rao(decoded.get("tao_emission"))
211
213
  drain = int(decoded.get("drain"))
212
214
  is_registered = bool(decoded.get("is_registered"))
213
215
 
214
- return StakeInfo(
215
- hotkey, coldkey, netuid, stake, locked, emission, drain, is_registered
216
+ return cls(
217
+ hotkey,
218
+ coldkey,
219
+ netuid,
220
+ stake,
221
+ locked,
222
+ emission,
223
+ tao_emission,
224
+ drain,
225
+ is_registered,
216
226
  )
217
227
 
218
228
 
@@ -293,7 +303,7 @@ class NeuronInfo(InfoBase):
293
303
  axon_info = decoded.get("axon_info", {})
294
304
  coldkey = decode_account_id(decoded.get("coldkey"))
295
305
  hotkey = decode_account_id(decoded.get("hotkey"))
296
- return NeuronInfo(
306
+ return cls(
297
307
  hotkey=hotkey,
298
308
  coldkey=coldkey,
299
309
  uid=decoded.get("uid"),
@@ -555,7 +565,7 @@ class SubnetInfo(InfoBase):
555
565
 
556
566
  @classmethod
557
567
  def _fix_decoded(cls, decoded: "SubnetInfo") -> "SubnetInfo":
558
- return SubnetInfo(
568
+ return cls(
559
569
  netuid=decoded.get("netuid"),
560
570
  rho=decoded.get("rho"),
561
571
  kappa=decoded.get("kappa"),
@@ -594,7 +604,7 @@ class SubnetIdentity(InfoBase):
594
604
 
595
605
  @classmethod
596
606
  def _fix_decoded(cls, decoded: dict) -> "SubnetIdentity":
597
- return SubnetIdentity(
607
+ return cls(
598
608
  subnet_name=bytes(decoded["subnet_name"]).decode(),
599
609
  github_repo=bytes(decoded["github_repo"]).decode(),
600
610
  subnet_contact=bytes(decoded["subnet_contact"]).decode(),
@@ -828,7 +838,7 @@ class SubnetState(InfoBase):
828
838
  @classmethod
829
839
  def _fix_decoded(cls, decoded: Any) -> "SubnetState":
830
840
  netuid = decoded.get("netuid")
831
- return SubnetState(
841
+ return cls(
832
842
  netuid=netuid,
833
843
  hotkeys=[decode_account_id(val) for val in decoded.get("hotkeys")],
834
844
  coldkeys=[decode_account_id(val) for val in decoded.get("coldkeys")],
@@ -112,8 +112,8 @@ class SubtensorInterface:
112
112
  f"[yellow]Connecting to Substrate:[/yellow][bold white] {self}..."
113
113
  ):
114
114
  try:
115
- async with self.substrate:
116
- return self
115
+ await self.substrate.initialize()
116
+ return self
117
117
  except TimeoutError: # TODO verify
118
118
  err_console.print(
119
119
  "\n[red]Error[/red]: Timeout occurred connecting to substrate. "
@@ -352,7 +352,7 @@ class SubtensorInterface:
352
352
  *ss58_addresses,
353
353
  block_hash: Optional[str] = None,
354
354
  reuse_block: bool = False,
355
- ) -> dict[str, Balance]:
355
+ ) -> dict[str, tuple[Balance, Balance]]:
356
356
  """
357
357
  Returns the total stake held on a coldkey.
358
358
 
@@ -370,7 +370,8 @@ class SubtensorInterface:
370
370
 
371
371
  results = {}
372
372
  for ss58, stake_info_list in sub_stakes.items():
373
- all_staked_tao = 0
373
+ total_tao_value = Balance(0)
374
+ total_swapped_tao_value = Balance(0)
374
375
  for sub_stake in stake_info_list:
375
376
  if sub_stake.stake.rao == 0:
376
377
  continue
@@ -381,19 +382,20 @@ class SubtensorInterface:
381
382
  netuid
382
383
  )
383
384
 
384
- tao_locked = pool.tao_in
385
-
386
- issuance = pool.alpha_out if pool.is_dynamic else tao_locked
387
- tao_ownership = Balance(0)
385
+ # Without slippage
386
+ tao_value = pool.alpha_to_tao(alpha_value)
387
+ total_tao_value += tao_value
388
388
 
389
- if alpha_value.tao > 0.00009 and issuance.tao != 0:
390
- tao_ownership = Balance.from_tao(
391
- (alpha_value.tao / issuance.tao) * tao_locked.tao
389
+ # With slippage
390
+ if netuid == 0:
391
+ swapped_tao_value = tao_value
392
+ else:
393
+ swapped_tao_value, _, _ = pool.alpha_to_tao_with_slippage(
394
+ sub_stake.stake
392
395
  )
396
+ total_swapped_tao_value += swapped_tao_value
393
397
 
394
- all_staked_tao += tao_ownership.rao
395
-
396
- results[ss58] = Balance.from_rao(all_staked_tao)
398
+ results[ss58] = (total_tao_value, total_swapped_tao_value)
397
399
  return results
398
400
 
399
401
  async def get_total_stake_for_hotkey(
@@ -1325,20 +1327,29 @@ class SubtensorInterface:
1325
1327
  This function is useful for analyzing the stake distribution and delegation patterns of multiple
1326
1328
  accounts simultaneously, offering a broader perspective on network participation and investment strategies.
1327
1329
  """
1328
- result = await self.query_runtime_api(
1329
- runtime_api="StakeInfoRuntimeApi",
1330
- method="get_stake_info_for_coldkeys",
1331
- params=[coldkey_ss58_list],
1332
- block_hash=block_hash,
1333
- )
1334
- if result is None:
1335
- return None
1330
+ BATCH_SIZE = 60
1336
1331
 
1332
+ tasks = []
1333
+ for i in range(0, len(coldkey_ss58_list), BATCH_SIZE):
1334
+ ss58_chunk = coldkey_ss58_list[i : i + BATCH_SIZE]
1335
+ tasks.append(
1336
+ self.query_runtime_api(
1337
+ runtime_api="StakeInfoRuntimeApi",
1338
+ method="get_stake_info_for_coldkeys",
1339
+ params=[ss58_chunk],
1340
+ block_hash=block_hash,
1341
+ )
1342
+ )
1343
+ results = await asyncio.gather(*tasks)
1337
1344
  stake_info_map = {}
1338
- for coldkey_bytes, stake_info_list in result:
1339
- coldkey_ss58 = decode_account_id(coldkey_bytes)
1340
- stake_info_map[coldkey_ss58] = StakeInfo.list_from_any(stake_info_list)
1341
- return stake_info_map
1345
+ for result in results:
1346
+ if result is None:
1347
+ continue
1348
+ for coldkey_bytes, stake_info_list in result:
1349
+ coldkey_ss58 = decode_account_id(coldkey_bytes)
1350
+ stake_info_map[coldkey_ss58] = StakeInfo.list_from_any(stake_info_list)
1351
+
1352
+ return stake_info_map if stake_info_map else None
1342
1353
 
1343
1354
  async def all_subnets(self, block_hash: Optional[str] = None) -> list[DynamicInfo]:
1344
1355
  result = await self.query_runtime_api(
@@ -17,6 +17,7 @@ from bittensor_cli.src.bittensor.utils import (
17
17
  is_valid_ss58_address,
18
18
  print_error,
19
19
  print_verbose,
20
+ unlock_key,
20
21
  )
21
22
  from bittensor_wallet import Wallet
22
23
  from bittensor_wallet.errors import KeyFileError
@@ -107,9 +108,7 @@ async def stake_add(
107
108
  )
108
109
  return
109
110
  else:
110
- err_out(
111
- f"\n{failure_prelude} with error: {format_error_message(e)}"
112
- )
111
+ err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
113
112
  return
114
113
  else:
115
114
  await response.process_events()
@@ -180,9 +179,7 @@ async def stake_add(
180
179
  extrinsic, wait_for_inclusion=True, wait_for_finalization=False
181
180
  )
182
181
  except SubstrateRequestException as e:
183
- err_out(
184
- f"\n{failure_prelude} with error: {format_error_message(e)}"
185
- )
182
+ err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
186
183
  return
187
184
  else:
188
185
  await response.process_events()
@@ -342,10 +339,7 @@ async def stake_add(
342
339
  if prompt:
343
340
  if not Confirm.ask("Would you like to continue?"):
344
341
  raise typer.Exit()
345
- try:
346
- wallet.unlock_coldkey()
347
- except KeyFileError:
348
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
342
+ if not unlock_key(wallet).success:
349
343
  return False
350
344
 
351
345
  if safe_staking:
@@ -256,7 +256,7 @@ async def get_childkey_take(subtensor, hotkey: str, netuid: int) -> Optional[int
256
256
  params=[hotkey, netuid],
257
257
  )
258
258
  if childkey_take_:
259
- return int(childkey_take_.value)
259
+ return int(childkey_take_)
260
260
 
261
261
  except SubstrateRequestException as e:
262
262
  err_console.print(f"Error querying ChildKeys: {format_error_message(e)}")
@@ -671,6 +671,8 @@ async def childkey_take(
671
671
  chk_take = await get_childkey_take(
672
672
  subtensor=subtensor, netuid=netuid, hotkey=ss58
673
673
  )
674
+ if chk_take is None:
675
+ chk_take = 0
674
676
  chk_take = u16_to_float(chk_take)
675
677
  console.print(
676
678
  f"Child take for {ss58} is: {chk_take * 100:.2f}% on netuid {netuid}."