bittensor-cli 8.4.2__py3-none-any.whl → 9.0.0rc1__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.
Files changed (30) hide show
  1. bittensor_cli/__init__.py +2 -2
  2. bittensor_cli/cli.py +1503 -1372
  3. bittensor_cli/src/__init__.py +625 -197
  4. bittensor_cli/src/bittensor/balances.py +41 -8
  5. bittensor_cli/src/bittensor/chain_data.py +557 -428
  6. bittensor_cli/src/bittensor/extrinsics/registration.py +161 -47
  7. bittensor_cli/src/bittensor/extrinsics/root.py +14 -8
  8. bittensor_cli/src/bittensor/extrinsics/transfer.py +14 -21
  9. bittensor_cli/src/bittensor/minigraph.py +46 -8
  10. bittensor_cli/src/bittensor/subtensor_interface.py +572 -253
  11. bittensor_cli/src/bittensor/utils.py +326 -75
  12. bittensor_cli/src/commands/stake/__init__.py +154 -0
  13. bittensor_cli/src/commands/stake/children_hotkeys.py +123 -91
  14. bittensor_cli/src/commands/stake/move.py +1000 -0
  15. bittensor_cli/src/commands/stake/stake.py +1637 -1264
  16. bittensor_cli/src/commands/subnets/__init__.py +0 -0
  17. bittensor_cli/src/commands/subnets/price.py +867 -0
  18. bittensor_cli/src/commands/subnets/subnets.py +2043 -0
  19. bittensor_cli/src/commands/sudo.py +529 -26
  20. bittensor_cli/src/commands/wallets.py +231 -535
  21. bittensor_cli/src/commands/weights.py +15 -11
  22. {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/METADATA +7 -4
  23. bittensor_cli-9.0.0rc1.dist-info/RECORD +32 -0
  24. bittensor_cli/src/bittensor/async_substrate_interface.py +0 -2748
  25. bittensor_cli/src/commands/root.py +0 -1752
  26. bittensor_cli/src/commands/subnets.py +0 -897
  27. bittensor_cli-8.4.2.dist-info/RECORD +0 -31
  28. {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/WHEEL +0 -0
  29. {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/entry_points.txt +0 -0
  30. {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -2,10 +2,11 @@ import asyncio
2
2
  from typing import Optional
3
3
 
4
4
  from bittensor_wallet import Wallet
5
+ from bittensor_wallet.errors import KeyFileError
5
6
  from rich.prompt import Confirm, Prompt, IntPrompt
6
7
  from rich.table import Table
7
8
  from rich.text import Text
8
- from substrateinterface.exceptions import SubstrateRequestException
9
+ from async_substrate_interface.errors import SubstrateRequestException
9
10
 
10
11
  from bittensor_cli.src.bittensor.balances import Balance
11
12
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
@@ -18,10 +19,36 @@ from bittensor_cli.src.bittensor.utils import (
18
19
  u64_to_float,
19
20
  is_valid_ss58_address,
20
21
  format_error_message,
21
- unlock_key,
22
22
  )
23
23
 
24
24
 
25
+ async def get_childkey_completion_block(
26
+ subtensor: SubtensorInterface, netuid: int
27
+ ) -> tuple[int, int]:
28
+ """
29
+ Calculates the block at which the childkey set request will complete
30
+ """
31
+ blocks_since_last_step_query = subtensor.query(
32
+ "SubtensorModule",
33
+ "BlocksSinceLastStep",
34
+ params=[netuid],
35
+ )
36
+ tempo_query = subtensor.get_hyperparameter(
37
+ param_name="Tempo",
38
+ netuid=netuid,
39
+ )
40
+ block_number, blocks_since_last_step, tempo = await asyncio.gather(
41
+ subtensor.substrate.get_block_number(),
42
+ blocks_since_last_step_query,
43
+ tempo_query,
44
+ )
45
+ cooldown = block_number + 1
46
+ blocks_left_in_tempo = tempo - blocks_since_last_step
47
+ next_tempo = block_number + blocks_left_in_tempo
48
+ next_epoch_after_cooldown = (cooldown - next_tempo) % tempo + cooldown
49
+ return block_number, next_epoch_after_cooldown
50
+
51
+
25
52
  async def set_children_extrinsic(
26
53
  subtensor: "SubtensorInterface",
27
54
  wallet: Wallet,
@@ -72,8 +99,10 @@ async def set_children_extrinsic(
72
99
  return False, "Operation Cancelled"
73
100
 
74
101
  # Decrypt coldkey.
75
- if not (unlock_status := unlock_key(wallet, print_out=False)).success:
76
- return False, unlock_status.message
102
+ try:
103
+ wallet.unlock_coldkey()
104
+ except KeyFileError:
105
+ return False, "There was an error unlocking your coldkey."
77
106
 
78
107
  with console.status(
79
108
  f":satellite: {operation} on [white]{subtensor.network}[/white] ..."
@@ -156,8 +185,10 @@ async def set_childkey_take_extrinsic(
156
185
  return False, "Operation Cancelled"
157
186
 
158
187
  # Decrypt coldkey.
159
- if not (unlock_status := unlock_key(wallet, print_out=False)).success:
160
- return False, unlock_status.message
188
+ try:
189
+ wallet.unlock_coldkey()
190
+ except KeyFileError:
191
+ return False, "There was an error unlocking your coldkey."
161
192
 
162
193
  with console.status(
163
194
  f":satellite: Setting childkey take on [white]{subtensor.network}[/white] ..."
@@ -208,7 +239,7 @@ async def set_childkey_take_extrinsic(
208
239
  except SubstrateRequestException as e:
209
240
  return (
210
241
  False,
211
- f"Exception occurred while setting childkey take: {format_error_message(e)}",
242
+ f"Exception occurred while setting childkey take: {format_error_message(e, subtensor.substrate)}",
212
243
  )
213
244
 
214
245
 
@@ -223,16 +254,18 @@ async def get_childkey_take(subtensor, hotkey: str, netuid: int) -> Optional[int
223
254
  - Optional[float]: The value of the "ChildkeyTake" if found, or None if any error occurs.
224
255
  """
225
256
  try:
226
- childkey_take_ = await subtensor.substrate.query(
257
+ childkey_take_ = await subtensor.query(
227
258
  module="SubtensorModule",
228
259
  storage_function="ChildkeyTake",
229
260
  params=[hotkey, netuid],
230
261
  )
231
262
  if childkey_take_:
232
- return int(childkey_take_)
263
+ return int(childkey_take_.value)
233
264
 
234
265
  except SubstrateRequestException as e:
235
- err_console.print(f"Error querying ChildKeys: {format_error_message(e)}")
266
+ err_console.print(
267
+ f"Error querying ChildKeys: {format_error_message(e, subtensor.substrate)}"
268
+ )
236
269
  return None
237
270
 
238
271
 
@@ -264,6 +297,7 @@ def prepare_child_proportions(children_with_proportions):
264
297
  async def get_children(
265
298
  wallet: Wallet, subtensor: "SubtensorInterface", netuid: Optional[int] = None
266
299
  ):
300
+ # TODO rao asks separately for the hotkey from the user, should we do this, or the way we do it now?
267
301
  """
268
302
  Retrieves the child hotkeys for the specified wallet.
269
303
 
@@ -281,39 +315,7 @@ async def get_children(
281
315
  - If netuid is not specified, generates and prints a summary table of all child hotkeys across all subnets.
282
316
  """
283
317
 
284
- async def get_total_stake_for_hk(hotkey: str, parent: bool = False):
285
- """
286
- Fetches and displays the total stake for a specified hotkey from the Subtensor blockchain network.
287
- If `parent` is True, it prints the hotkey and its corresponding stake.
288
-
289
- Parameters:
290
- - hotkey (str): The hotkey for which the stake needs to be fetched.
291
- - parent (bool, optional): A flag to indicate whether the hotkey is the parent key. Defaults to False.
292
-
293
- Returns:
294
- - Balance: The total stake associated with the specified hotkey.
295
- """
296
- _result = await subtensor.substrate.query(
297
- module="SubtensorModule",
298
- storage_function="TotalHotkeyStake",
299
- params=[hotkey],
300
- reuse_block_hash=True,
301
- )
302
- stake = (
303
- Balance.from_rao(_result)
304
- if _result is not None
305
- else Balance(0)
306
- )
307
- if parent:
308
- console.print(
309
- f"\nYour Hotkey: [bright_magenta]{hotkey}[/bright_magenta] | Total Stake: [dark_orange]{stake}t[/dark_orange]\n",
310
- end="",
311
- no_wrap=True,
312
- )
313
-
314
- return stake
315
-
316
- async def get_take(child: tuple) -> float:
318
+ async def get_take(child: tuple, netuid__: int) -> float:
317
319
  """
318
320
  Get the take value for a given subtensor, hotkey, and netuid.
319
321
 
@@ -324,7 +326,7 @@ async def get_children(
324
326
  """
325
327
  child_hotkey = child[1]
326
328
  take_u16 = await get_childkey_take(
327
- subtensor=subtensor, hotkey=child_hotkey, netuid=netuid
329
+ subtensor=subtensor, hotkey=child_hotkey, netuid=netuid__
328
330
  )
329
331
  if take_u16:
330
332
  return u16_to_float(take_u16)
@@ -333,7 +335,7 @@ async def get_children(
333
335
 
334
336
  async def _render_table(
335
337
  parent_hotkey: str,
336
- netuid_children_tuples: list[tuple[int, list[tuple[int, str]]]],
338
+ netuid_children_: list[tuple[int, list[tuple[int, str]]]],
337
339
  ):
338
340
  """
339
341
  Retrieves and renders children hotkeys and their details for a given parent hotkey.
@@ -356,10 +358,11 @@ async def get_children(
356
358
  "Current Stake Weight", style="bold red", no_wrap=True, justify="right"
357
359
  )
358
360
 
359
- if not netuid_children_tuples:
361
+ if not netuid_children_:
360
362
  console.print(table)
361
363
  console.print(
362
- f"[bold red]There are currently no child hotkeys with parent hotkey: {wallet.name} ({parent_hotkey}).[/bold red]"
364
+ f"[bold red]There are currently no child hotkeys with parent hotkey: "
365
+ f"{wallet.name} | {wallet.hotkey_str} ({parent_hotkey}).[/bold red]"
363
366
  )
364
367
  return
365
368
 
@@ -367,48 +370,64 @@ async def get_children(
367
370
  total_proportion = 0
368
371
  total_stake_weight = 0
369
372
 
370
- netuid_children_tuples.sort(
371
- key=lambda x: x[0]
372
- ) # Sort by netuid in ascending order
373
+ netuid_children_.sort(key=lambda x: x[0]) # Sort by netuid in ascending order
374
+ unique_keys = set(
375
+ [parent_hotkey]
376
+ + [s for _, child_list in netuid_children_ for _, s in child_list]
377
+ )
378
+ hotkey_stake_dict = await subtensor.get_total_stake_for_hotkey(
379
+ *unique_keys,
380
+ netuids=[n[0] for n in netuid_children_],
381
+ )
382
+ parent_total = sum(hotkey_stake_dict[parent_hotkey].values())
383
+ insert_text = (
384
+ " "
385
+ if netuid is None
386
+ else f" on netuids: {', '.join(str(n[0]) for n in netuid_children_)} "
387
+ )
388
+ console.print(
389
+ f"The total stake of parent hotkey '{parent_hotkey}'{insert_text}is {parent_total}."
390
+ )
373
391
 
374
- for index, (netuid, children_) in enumerate(netuid_children_tuples):
392
+ for index, (netuid_, children_) in enumerate(netuid_children_):
375
393
  # calculate totals
376
394
  total_proportion_per_netuid = 0
377
395
  total_stake_weight_per_netuid = 0
378
- avg_take_per_netuid = 0
396
+ avg_take_per_netuid = 0.0
379
397
 
380
- hotkey_stake_dict = await subtensor.get_total_stake_for_hotkey(
381
- parent_hotkey
382
- )
383
- hotkey_stake = hotkey_stake_dict.get(parent_hotkey, Balance(0))
398
+ hotkey_stake: dict[int, Balance] = hotkey_stake_dict[parent_hotkey]
384
399
 
385
400
  children_info = []
386
- child_stakes = await asyncio.gather(
387
- *[get_total_stake_for_hk(c[1]) for c in children_]
401
+ child_takes = await asyncio.gather(
402
+ *[get_take(c, netuid_) for c in children_]
388
403
  )
389
- child_takes = await asyncio.gather(*[get_take(c) for c in children_])
390
- for child, child_stake, child_take in zip(
391
- children_, child_stakes, child_takes
392
- ):
404
+ for child, child_take in zip(children_, child_takes):
393
405
  proportion = child[0]
394
406
  child_hotkey = child[1]
395
407
 
396
408
  # add to totals
397
409
  avg_take_per_netuid += child_take
398
410
 
399
- proportion = u64_to_float(proportion)
411
+ converted_proportion = u64_to_float(proportion)
400
412
 
401
413
  children_info.append(
402
- (proportion, child_hotkey, child_stake, child_take)
414
+ (
415
+ converted_proportion,
416
+ child_hotkey,
417
+ hotkey_stake_dict[child_hotkey][netuid_],
418
+ child_take,
419
+ )
403
420
  )
404
421
 
405
422
  children_info.sort(
406
423
  key=lambda x: x[0], reverse=True
407
424
  ) # sorting by proportion (highest first)
408
425
 
409
- for proportion, hotkey, stake, child_take in children_info:
410
- proportion_percent = proportion * 100 # Proportion in percent
411
- proportion_tao = hotkey_stake.tao * proportion # Proportion in TAO
426
+ for proportion_, hotkey, stake, child_take in children_info:
427
+ proportion_percent = proportion_ * 100 # Proportion in percent
428
+ proportion_tao = (
429
+ hotkey_stake[netuid_].tao * proportion_
430
+ ) # Proportion in TAO
412
431
 
413
432
  total_proportion_per_netuid += proportion_percent
414
433
 
@@ -418,9 +437,9 @@ async def get_children(
418
437
  total_stake_weight_per_netuid += stake_weight
419
438
  take_str = f"{child_take * 100:.3f}%"
420
439
 
421
- hotkey = Text(hotkey, style="italic red" if proportion == 0 else "")
440
+ hotkey = Text(hotkey, style="italic red" if proportion_ == 0 else "")
422
441
  table.add_row(
423
- str(netuid),
442
+ str(netuid_),
424
443
  hotkey,
425
444
  proportion_str,
426
445
  take_str,
@@ -444,7 +463,7 @@ async def get_children(
444
463
  total_stake_weight += total_stake_weight_per_netuid
445
464
 
446
465
  # Add a dividing line if there are more than one netuid
447
- if len(netuid_children_tuples) > 1:
466
+ if len(netuid_children_) > 1:
448
467
  table.add_section()
449
468
 
450
469
  console.print(table)
@@ -453,17 +472,16 @@ async def get_children(
453
472
  if netuid is None:
454
473
  # get all netuids
455
474
  netuids = await subtensor.get_all_subnet_netuids()
456
- await get_total_stake_for_hk(wallet.hotkey.ss58_address, True)
457
475
  netuid_children_tuples = []
458
- for netuid in netuids:
476
+ for netuid_ in netuids:
459
477
  success, children, err_mg = await subtensor.get_children(
460
- wallet.hotkey.ss58_address, netuid
478
+ wallet.hotkey.ss58_address, netuid_
461
479
  )
462
480
  if children:
463
- netuid_children_tuples.append((netuid, children))
481
+ netuid_children_tuples.append((netuid_, children))
464
482
  if not success:
465
483
  err_console.print(
466
- f"Failed to get children from subtensor {netuid}: {err_mg}"
484
+ f"Failed to get children from subtensor {netuid_}: {err_mg}"
467
485
  )
468
486
  await _render_table(wallet.hotkey.ss58_address, netuid_children_tuples)
469
487
  else:
@@ -472,7 +490,6 @@ async def get_children(
472
490
  )
473
491
  if not success:
474
492
  err_console.print(f"Failed to get children from subtensor: {err_mg}")
475
- await get_total_stake_for_hk(wallet.hotkey.ss58_address, True)
476
493
  if children:
477
494
  netuid_children_tuples = [(netuid, children)]
478
495
  await _render_table(wallet.hotkey.ss58_address, netuid_children_tuples)
@@ -485,13 +502,15 @@ async def set_children(
485
502
  subtensor: "SubtensorInterface",
486
503
  children: list[str],
487
504
  proportions: list[float],
488
- netuid: Optional[int] = None,
505
+ netuid: Optional[int],
489
506
  wait_for_inclusion: bool = True,
490
507
  wait_for_finalization: bool = True,
491
508
  prompt: bool = True,
492
509
  ):
493
510
  """Set children hotkeys."""
494
511
  # Validate children SS58 addresses
512
+ # TODO check to see if this should be allowed to be specified by user instead of pulling from wallet
513
+ hotkey = wallet.hotkey.ss58_address
495
514
  for child in children:
496
515
  if not is_valid_ss58_address(child):
497
516
  err_console.print(f":cross_mark:[red] Invalid SS58 address: {child}[/red]")
@@ -506,14 +525,13 @@ async def set_children(
506
525
  f"Invalid proportion: The sum of all proportions cannot be greater than 1. "
507
526
  f"Proposed sum of proportions is {total_proposed}."
508
527
  )
509
-
510
528
  children_with_proportions = list(zip(proportions, children))
511
- if netuid:
529
+ if netuid is not None:
512
530
  success, message = await set_children_extrinsic(
513
531
  subtensor=subtensor,
514
532
  wallet=wallet,
515
533
  netuid=netuid,
516
- hotkey=wallet.hotkey.ss58_address,
534
+ hotkey=hotkey,
517
535
  children_with_proportions=children_with_proportions,
518
536
  prompt=prompt,
519
537
  wait_for_inclusion=wait_for_inclusion,
@@ -522,8 +540,14 @@ async def set_children(
522
540
  # Result
523
541
  if success:
524
542
  if wait_for_inclusion and wait_for_finalization:
525
- console.print("New Status:")
526
- await get_children(wallet, subtensor, netuid)
543
+ current_block, completion_block = await get_childkey_completion_block(
544
+ subtensor, netuid
545
+ )
546
+ console.print(
547
+ f"Your childkey request has been submitted. It will be completed around block {completion_block}, "
548
+ f"assuming you have the required key swap cost (default: 0.1 Tao) in your coldkey at that time. "
549
+ f"The current block is {current_block}"
550
+ )
527
551
  console.print(
528
552
  ":white_heavy_check_mark: [green]Set children hotkeys.[/green]"
529
553
  )
@@ -534,20 +558,28 @@ async def set_children(
534
558
  else:
535
559
  # set children on all subnets that parent is registered on
536
560
  netuids = await subtensor.get_all_subnet_netuids()
537
- for netuid in netuids:
538
- if netuid == 0: # dont include root network
561
+ for netuid_ in netuids:
562
+ if netuid_ == 0: # dont include root network
539
563
  continue
540
- console.print(f"Setting children on netuid {netuid}.")
564
+ console.print(f"Setting children on netuid {netuid_}.")
541
565
  await set_children_extrinsic(
542
566
  subtensor=subtensor,
543
567
  wallet=wallet,
544
- netuid=netuid,
545
- hotkey=wallet.hotkey.ss58_address,
568
+ netuid=netuid_,
569
+ hotkey=hotkey,
546
570
  children_with_proportions=children_with_proportions,
547
571
  prompt=prompt,
548
572
  wait_for_inclusion=True,
549
573
  wait_for_finalization=False,
550
574
  )
575
+ current_block, completion_block = await get_childkey_completion_block(
576
+ subtensor, netuid_
577
+ )
578
+ console.print(
579
+ f"Your childkey request for netuid {netuid_} has been submitted. It will be completed around "
580
+ f"block {completion_block}, assuming you have the required key swap cost (default: 0.1 Tao) in your "
581
+ f"coldkey at that time. The current block is {current_block}."
582
+ )
551
583
  console.print(
552
584
  ":white_heavy_check_mark: [green]Sent set children request for all subnets.[/green]"
553
585
  )
@@ -559,8 +591,8 @@ async def revoke_children(
559
591
  netuid: Optional[int] = None,
560
592
  wait_for_inclusion: bool = True,
561
593
  wait_for_finalization: bool = True,
562
- prompt: bool = True,
563
594
  ):
595
+ # TODO seek clarification on use of asking hotkey vs how we do it now
564
596
  """
565
597
  Revokes the children hotkeys associated with a given network identifier (netuid).
566
598
  """
@@ -571,7 +603,7 @@ async def revoke_children(
571
603
  netuid=netuid,
572
604
  hotkey=wallet.hotkey.ss58_address,
573
605
  children_with_proportions=[],
574
- prompt=prompt,
606
+ prompt=True,
575
607
  wait_for_inclusion=wait_for_inclusion,
576
608
  wait_for_finalization=wait_for_finalization,
577
609
  )
@@ -600,7 +632,7 @@ async def revoke_children(
600
632
  netuid=netuid,
601
633
  hotkey=wallet.hotkey.ss58_address,
602
634
  children_with_proportions=[],
603
- prompt=prompt,
635
+ prompt=False,
604
636
  wait_for_inclusion=True,
605
637
  wait_for_finalization=False,
606
638
  )
@@ -760,7 +792,7 @@ async def childkey_take(
760
792
  netuid=netuid,
761
793
  hotkey=wallet.hotkey.ss58_address,
762
794
  take=take,
763
- prompt=prompt,
795
+ prompt=False,
764
796
  wait_for_inclusion=True,
765
797
  wait_for_finalization=False,
766
798
  )