bittensor-cli 9.2.0__py3-none-any.whl → 9.4.0__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.
@@ -10,7 +10,7 @@ from rich.progress import Progress, BarColumn, TextColumn
10
10
  from rich.table import Column, Table
11
11
  from rich import box
12
12
 
13
- from bittensor_cli.src import COLOR_PALETTE
13
+ from bittensor_cli.src import COLOR_PALETTE, Constants
14
14
  from bittensor_cli.src.bittensor.balances import Balance
15
15
  from bittensor_cli.src.bittensor.extrinsics.registration import (
16
16
  register_extrinsic,
@@ -34,6 +34,8 @@ from bittensor_cli.src.bittensor.utils import (
34
34
  prompt_for_identity,
35
35
  get_subnet_name,
36
36
  unlock_key,
37
+ blocks_to_duration,
38
+ json_console,
37
39
  )
38
40
 
39
41
  if TYPE_CHECKING:
@@ -51,7 +53,7 @@ async def register_subnetwork_extrinsic(
51
53
  wait_for_inclusion: bool = False,
52
54
  wait_for_finalization: bool = True,
53
55
  prompt: bool = False,
54
- ) -> bool:
56
+ ) -> tuple[bool, Optional[int]]:
55
57
  """Registers a new subnetwork.
56
58
 
57
59
  wallet (bittensor.wallet):
@@ -100,7 +102,7 @@ async def register_subnetwork_extrinsic(
100
102
  f"[{COLOR_PALETTE['POOLS']['TAO']}]{sn_burn_cost}[{COLOR_PALETTE['POOLS']['TAO']}] "
101
103
  f"to register a subnet."
102
104
  )
103
- return False
105
+ return False, None
104
106
 
105
107
  if prompt:
106
108
  console.print(
@@ -109,7 +111,7 @@ async def register_subnetwork_extrinsic(
109
111
  if not Confirm.ask(
110
112
  f"Do you want to burn [{COLOR_PALETTE['POOLS']['TAO']}]{sn_burn_cost} to register a subnet?"
111
113
  ):
112
- return False
114
+ return False, None
113
115
 
114
116
  call_params = {
115
117
  "hotkey": wallet.hotkey.ss58_address,
@@ -151,10 +153,10 @@ async def register_subnetwork_extrinsic(
151
153
  f"[red]Error:[/red] Identity field [white]{field}[/white] must be <= {max_size} bytes.\n"
152
154
  f"Value '{value.decode()}' is {len(value)} bytes."
153
155
  )
154
- return False
156
+ return False, None
155
157
 
156
158
  if not unlock_key(wallet).success:
157
- return False
159
+ return False, None
158
160
 
159
161
  with console.status(":satellite: Registering subnet...", spinner="earth"):
160
162
  substrate = subtensor.substrate
@@ -175,15 +177,14 @@ async def register_subnetwork_extrinsic(
175
177
 
176
178
  # We only wait here if we expect finalization.
177
179
  if not wait_for_finalization and not wait_for_inclusion:
178
- return True
180
+ return True, None
179
181
 
180
- await response.process_events()
181
182
  if not await response.is_success:
182
183
  err_console.print(
183
184
  f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}"
184
185
  )
185
186
  await asyncio.sleep(0.5)
186
- return False
187
+ return False, None
187
188
 
188
189
  # Successful registration, final check for membership
189
190
  else:
@@ -193,7 +194,7 @@ async def register_subnetwork_extrinsic(
193
194
  console.print(
194
195
  f":white_heavy_check_mark: [dark_sea_green3]Registered subnetwork with netuid: {attributes[0]}"
195
196
  )
196
- return True
197
+ return True, int(attributes[0])
197
198
 
198
199
 
199
200
  # commands
@@ -206,40 +207,41 @@ async def subnets_list(
206
207
  no_cache: bool,
207
208
  verbose: bool,
208
209
  live: bool,
210
+ json_output: bool,
209
211
  ):
210
212
  """List all subnet netuids in the network."""
211
213
 
212
214
  async def fetch_subnet_data():
213
- block_number = await subtensor.substrate.get_block_number(None)
214
- subnets = await subtensor.all_subnets()
215
+ block_number_ = await subtensor.substrate.get_block_number(None)
216
+ subnets_ = await subtensor.all_subnets()
215
217
 
216
218
  # Sort subnets by market cap, keeping the root subnet in the first position
217
- root_subnet = next(s for s in subnets if s.netuid == 0)
219
+ root_subnet = next(s for s in subnets_ if s.netuid == 0)
218
220
  other_subnets = sorted(
219
- [s for s in subnets if s.netuid != 0],
221
+ [s for s in subnets_ if s.netuid != 0],
220
222
  key=lambda x: (x.alpha_in.tao + x.alpha_out.tao) * x.price.tao,
221
223
  reverse=True,
222
224
  )
223
225
  sorted_subnets = [root_subnet] + other_subnets
224
- return sorted_subnets, block_number
226
+ return sorted_subnets, block_number_
225
227
 
226
228
  def calculate_emission_stats(
227
- subnets: list, block_number: int
229
+ subnets_: list, block_number_: int
228
230
  ) -> tuple[Balance, str]:
229
231
  # We do not include the root subnet in the emission calculation
230
232
  total_tao_emitted = sum(
231
- subnet.tao_in.tao for subnet in subnets if subnet.netuid != 0
233
+ subnet.tao_in.tao for subnet in subnets_ if subnet.netuid != 0
232
234
  )
233
- emission_percentage = (total_tao_emitted / block_number) * 100
235
+ emission_percentage = (total_tao_emitted / block_number_) * 100
234
236
  percentage_color = "dark_sea_green" if emission_percentage < 100 else "red"
235
237
  formatted_percentage = (
236
238
  f"[{percentage_color}]{emission_percentage:.2f}%[/{percentage_color}]"
237
239
  )
238
240
  if not verbose:
239
- percentage_string = f"τ {millify_tao(total_tao_emitted)}/{millify_tao(block_number)} ({formatted_percentage})"
241
+ percentage_string = f"τ {millify_tao(total_tao_emitted)}/{millify_tao(block_number_)} ({formatted_percentage})"
240
242
  else:
241
243
  percentage_string = (
242
- f"τ {total_tao_emitted:.1f}/{block_number} ({formatted_percentage})"
244
+ f"τ {total_tao_emitted:.1f}/{block_number_} ({formatted_percentage})"
243
245
  )
244
246
  return total_tao_emitted, percentage_string
245
247
 
@@ -249,7 +251,7 @@ async def subnets_list(
249
251
  total_netuids: int,
250
252
  tao_emission_percentage: str,
251
253
  ):
252
- table = Table(
254
+ defined_table = Table(
253
255
  title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Subnets"
254
256
  f"\nNetwork: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{subtensor.network}\n\n",
255
257
  show_footer=True,
@@ -262,61 +264,61 @@ async def subnets_list(
262
264
  pad_edge=True,
263
265
  )
264
266
 
265
- table.add_column(
267
+ defined_table.add_column(
266
268
  "[bold white]Netuid",
267
269
  style="grey89",
268
270
  justify="center",
269
271
  footer=str(total_netuids),
270
272
  )
271
- table.add_column("[bold white]Name", style="cyan", justify="left")
272
- table.add_column(
273
+ defined_table.add_column("[bold white]Name", style="cyan", justify="left")
274
+ defined_table.add_column(
273
275
  f"[bold white]Price \n({Balance.get_unit(0)}_in/{Balance.get_unit(1)}_in)",
274
276
  style="dark_sea_green2",
275
277
  justify="left",
276
278
  footer=f"τ {total_rate}",
277
279
  )
278
- table.add_column(
280
+ defined_table.add_column(
279
281
  f"[bold white]Market Cap \n({Balance.get_unit(1)} * Price)",
280
282
  style="steel_blue3",
281
283
  justify="left",
282
284
  )
283
- table.add_column(
285
+ defined_table.add_column(
284
286
  f"[bold white]Emission ({Balance.get_unit(0)})",
285
287
  style=COLOR_PALETTE["POOLS"]["EMISSION"],
286
288
  justify="left",
287
289
  footer=f"τ {total_emissions}",
288
290
  )
289
- table.add_column(
291
+ defined_table.add_column(
290
292
  f"[bold white]P ({Balance.get_unit(0)}_in, {Balance.get_unit(1)}_in)",
291
293
  style=COLOR_PALETTE["STAKE"]["TAO"],
292
294
  justify="left",
293
295
  footer=f"{tao_emission_percentage}",
294
296
  )
295
- table.add_column(
297
+ defined_table.add_column(
296
298
  f"[bold white]Stake ({Balance.get_unit(1)}_out)",
297
299
  style=COLOR_PALETTE["STAKE"]["STAKE_ALPHA"],
298
300
  justify="left",
299
301
  )
300
- table.add_column(
302
+ defined_table.add_column(
301
303
  f"[bold white]Supply ({Balance.get_unit(1)})",
302
304
  style=COLOR_PALETTE["POOLS"]["ALPHA_IN"],
303
305
  justify="left",
304
306
  )
305
307
 
306
- table.add_column(
308
+ defined_table.add_column(
307
309
  "[bold white]Tempo (k/n)",
308
310
  style=COLOR_PALETTE["GENERAL"]["TEMPO"],
309
311
  justify="left",
310
312
  overflow="fold",
311
313
  )
312
- return table
314
+ return defined_table
313
315
 
314
316
  # Non-live mode
315
- def create_table(subnets, block_number):
317
+ def _create_table(subnets_, block_number_):
316
318
  rows = []
317
- _, percentage_string = calculate_emission_stats(subnets, block_number)
319
+ _, percentage_string = calculate_emission_stats(subnets_, block_number_)
318
320
 
319
- for subnet in subnets:
321
+ for subnet in subnets_:
320
322
  netuid = subnet.netuid
321
323
  symbol = f"{subnet.symbol}\u200e"
322
324
 
@@ -363,7 +365,7 @@ async def subnets_list(
363
365
  # Prepare cells
364
366
  netuid_cell = str(netuid)
365
367
  subnet_name_cell = (
366
- f"[{COLOR_PALETTE['GENERAL']['SYMBOL']}]{subnet.symbol if netuid != 0 else 'τ'}[/{COLOR_PALETTE['GENERAL']['SYMBOL']}]"
368
+ f"[{COLOR_PALETTE.G.SYM}]{subnet.symbol if netuid != 0 else 'τ'}[/{COLOR_PALETTE.G.SYM}]"
367
369
  f" {get_subnet_name(subnet)}"
368
370
  )
369
371
  emission_cell = f"τ {emission_tao:,.4f}"
@@ -396,23 +398,76 @@ async def subnets_list(
396
398
  )
397
399
 
398
400
  total_emissions = round(
399
- sum(subnet.tao_in_emission.tao for subnet in subnets if subnet.netuid != 0),
401
+ sum(
402
+ subnet.tao_in_emission.tao for subnet in subnets_ if subnet.netuid != 0
403
+ ),
400
404
  4,
401
405
  )
402
406
  total_rate = round(
403
- sum(float(subnet.price.tao) for subnet in subnets if subnet.netuid != 0), 4
407
+ sum(float(subnet.price.tao) for subnet in subnets_ if subnet.netuid != 0), 4
404
408
  )
405
- total_netuids = len(subnets)
406
- table = define_table(
409
+ total_netuids = len(subnets_)
410
+ defined_table = define_table(
407
411
  total_emissions, total_rate, total_netuids, percentage_string
408
412
  )
409
413
 
410
414
  for row in rows:
411
- table.add_row(*row)
412
- return table
415
+ defined_table.add_row(*row)
416
+ return defined_table
417
+
418
+ def dict_table(subnets_, block_number_) -> dict:
419
+ subnet_rows = {}
420
+ total_tao_emitted, _ = calculate_emission_stats(subnets_, block_number_)
421
+ total_emissions = 0.0
422
+ total_rate = 0.0
423
+ total_netuids = len(subnets_)
424
+ emission_percentage = (total_tao_emitted / block_number_) * 100
425
+ for subnet in subnets_:
426
+ total_emissions += subnet.tao_in_emission.tao
427
+ total_rate += subnet.price.tao
428
+ netuid = subnet.netuid
429
+ if netuid == 0:
430
+ emission_tao = 0.0
431
+ else:
432
+ emission_tao = subnet.tao_in_emission.tao
433
+ alpha_in_value = subnet.alpha_in.tao
434
+ alpha_out_value = subnet.alpha_out.tao
435
+ price_value = subnet.price.tao
436
+ market_cap = (subnet.alpha_in.tao + subnet.alpha_out.tao) * subnet.price.tao
437
+ tao_in = subnet.tao_in.tao if netuid != 0 else None
438
+ alpha_in = alpha_in_value if netuid != 0 else None
439
+ alpha_out = alpha_out_value if netuid != 0 else None
440
+ supply = subnet.alpha_in.tao + subnet.alpha_out.tao
441
+ subnet_name = get_subnet_name(subnet)
442
+ tempo = {
443
+ "blocks_since_last_step": (
444
+ subnet.blocks_since_last_step if netuid != 0 else None
445
+ ),
446
+ "sn_tempo": (subnet.tempo if netuid != 0 else None),
447
+ }
448
+ subnet_rows[netuid] = {
449
+ "netuid": netuid,
450
+ "subnet_name": subnet_name,
451
+ "price": price_value,
452
+ "market_cap": market_cap,
453
+ "emission": emission_tao,
454
+ "liquidity": {"tao_in": tao_in, "alpha_in": alpha_in},
455
+ "alpha_out": alpha_out,
456
+ "supply": supply,
457
+ "tempo": tempo,
458
+ }
459
+ output = {
460
+ "total_tao_emitted": total_tao_emitted,
461
+ "total_emissions": total_emissions,
462
+ "total_rate": total_rate,
463
+ "total_netuids": total_netuids,
464
+ "emission_percentage": emission_percentage,
465
+ "subnets": subnet_rows,
466
+ }
467
+ return output
413
468
 
414
469
  # Live mode
415
- def create_table_live(subnets, previous_data, block_number):
470
+ def create_table_live(subnets_, previous_data_, block_number_):
416
471
  def format_cell(
417
472
  value, previous_value, unit="", unit_first=False, precision=4, millify=False
418
473
  ):
@@ -516,9 +571,9 @@ async def subnets_list(
516
571
 
517
572
  rows = []
518
573
  current_data = {} # To store current values for comparison in the next update
519
- _, percentage_string = calculate_emission_stats(subnets, block_number)
574
+ _, percentage_string = calculate_emission_stats(subnets_, block_number_)
520
575
 
521
- for subnet in subnets:
576
+ for subnet in subnets_:
522
577
  netuid = subnet.netuid
523
578
  symbol = f"{subnet.symbol}\u200e"
524
579
 
@@ -541,7 +596,7 @@ async def subnets_list(
541
596
  "supply": supply,
542
597
  "blocks_since_last_step": subnet.blocks_since_last_step,
543
598
  }
544
- prev = previous_data.get(netuid, {}) if previous_data else {}
599
+ prev = previous_data_.get(netuid, {}) if previous_data_ else {}
545
600
 
546
601
  # Prepare cells
547
602
  if netuid == 0:
@@ -652,9 +707,9 @@ async def subnets_list(
652
707
  )
653
708
 
654
709
  # Calculate totals
655
- total_netuids = len(subnets)
710
+ total_netuids = len(subnets_)
656
711
  _total_emissions = sum(
657
- subnet.tao_in_emission.tao for subnet in subnets if subnet.netuid != 0
712
+ subnet.tao_in_emission.tao for subnet in subnets_ if subnet.netuid != 0
658
713
  )
659
714
  total_emissions = (
660
715
  f"{millify_tao(_total_emissions)}"
@@ -662,7 +717,7 @@ async def subnets_list(
662
717
  else f"{_total_emissions:,.2f}"
663
718
  )
664
719
 
665
- total_rate = sum(subnet.price.tao for subnet in subnets if subnet.netuid != 0)
720
+ total_rate = sum(subnet.price.tao for subnet in subnets_ if subnet.netuid != 0)
666
721
  total_rate = (
667
722
  f"{millify_tao(total_rate)}" if not verbose else f"{total_rate:,.2f}"
668
723
  )
@@ -733,8 +788,11 @@ async def subnets_list(
733
788
  else:
734
789
  # Non-live mode
735
790
  subnets, block_number = await fetch_subnet_data()
736
- table = create_table(subnets, block_number)
737
- console.print(table)
791
+ if json_output:
792
+ json_console.print(json.dumps(dict_table(subnets, block_number)))
793
+ else:
794
+ table = _create_table(subnets, block_number)
795
+ console.print(table)
738
796
 
739
797
  return
740
798
  # TODO: Temporarily returning till we update docs
@@ -804,20 +862,22 @@ async def show(
804
862
  delegate_selection: bool = False,
805
863
  verbose: bool = False,
806
864
  prompt: bool = True,
865
+ json_output: bool = False,
807
866
  ) -> Optional[str]:
808
867
  async def show_root():
868
+ # TODO json_output for this, don't forget
809
869
  block_hash = await subtensor.substrate.get_chain_head()
810
- all_subnets = await subtensor.all_subnets(block_hash=block_hash)
811
- root_info = next((s for s in all_subnets if s.netuid == 0), None)
812
- if root_info is None:
813
- print_error("The root subnet does not exist")
814
- return False
815
870
 
816
- root_state, identities, old_identities = await asyncio.gather(
871
+ all_subnets, root_state, identities, old_identities = await asyncio.gather(
872
+ subtensor.all_subnets(block_hash=block_hash),
817
873
  subtensor.get_subnet_state(netuid=0, block_hash=block_hash),
818
874
  subtensor.query_all_identities(block_hash=block_hash),
819
875
  subtensor.get_delegate_identities(block_hash=block_hash),
820
876
  )
877
+ root_info = next((s for s in all_subnets if s.netuid == 0), None)
878
+ if root_info is None:
879
+ print_error("The root subnet does not exist")
880
+ return False
821
881
 
822
882
  if root_state is None:
823
883
  err_console.print("The root subnet does not exist")
@@ -829,12 +889,11 @@ async def show(
829
889
  )
830
890
  return
831
891
 
832
- tao_sum = sum(
833
- [root_state.tao_stake[idx].tao for idx in range(len(root_state.tao_stake))]
834
- )
892
+ tao_sum = sum(root_state.tao_stake).tao
835
893
 
836
894
  table = Table(
837
- title=f"[{COLOR_PALETTE['GENERAL']['HEADER']}]Root Network\n[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]Network: {subtensor.network}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}]\n",
895
+ title=f"[{COLOR_PALETTE.G.HEADER}]Root Network\n[{COLOR_PALETTE.G.SUBHEAD}]"
896
+ f"Network: {subtensor.network}[/{COLOR_PALETTE.G.SUBHEAD}]\n",
838
897
  show_footer=True,
839
898
  show_edge=False,
840
899
  header_style="bold white",
@@ -1119,6 +1178,7 @@ async def show(
1119
1178
  )
1120
1179
 
1121
1180
  rows = []
1181
+ json_out_rows = []
1122
1182
  for idx in sorted_indices:
1123
1183
  # Get identity for this uid
1124
1184
  coldkey_identity = identities.get(subnet_state.coldkeys[idx], {}).get(
@@ -1170,6 +1230,22 @@ async def show(
1170
1230
  uid_identity, # Identity
1171
1231
  )
1172
1232
  )
1233
+ json_out_rows.append(
1234
+ {
1235
+ "uid": idx,
1236
+ "stake": subnet_state.total_stake[idx].tao,
1237
+ "alpha_stake": subnet_state.alpha_stake[idx].tao,
1238
+ "tao_stake": tao_stake.tao,
1239
+ "dividends": subnet_state.dividends[idx],
1240
+ "incentive": subnet_state.incentives[idx],
1241
+ "emissions": Balance.from_tao(subnet_state.emission[idx].tao)
1242
+ .set_unit(netuid_)
1243
+ .tao,
1244
+ "hotkey": subnet_state.hotkeys[idx],
1245
+ "coldkey": subnet_state.coldkeys[idx],
1246
+ "identity": uid_identity,
1247
+ }
1248
+ )
1173
1249
 
1174
1250
  # Add columns to the table
1175
1251
  table.add_column("UID", style="grey89", no_wrap=True, justify="center")
@@ -1262,6 +1338,24 @@ async def show(
1262
1338
  if current_burn_cost
1263
1339
  else Balance(0)
1264
1340
  )
1341
+ output_dict = {
1342
+ "netuid": netuid_,
1343
+ "name": subnet_name_display,
1344
+ "owner": subnet_info.owner_coldkey,
1345
+ "owner_identity": owner_identity,
1346
+ "rate": subnet_info.price.tao,
1347
+ "emission": subnet_info.emission.tao,
1348
+ "tao_pool": subnet_info.tao_in.tao,
1349
+ "alpha_pool": subnet_info.alpha_in.tao,
1350
+ "tempo": {
1351
+ "block_since_last_step": subnet_info.blocks_since_last_step,
1352
+ "tempo": subnet_info.tempo,
1353
+ },
1354
+ "registration_cost": current_registration_burn.tao,
1355
+ "uids": json_out_rows,
1356
+ }
1357
+ if json_output:
1358
+ json_console.print(json.dumps(output_dict))
1265
1359
 
1266
1360
  console.print(
1267
1361
  f"[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]Subnet {netuid_}{subnet_name_display}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
@@ -1334,7 +1428,9 @@ async def show(
1334
1428
  return result
1335
1429
 
1336
1430
 
1337
- async def burn_cost(subtensor: "SubtensorInterface") -> Optional[Balance]:
1431
+ async def burn_cost(
1432
+ subtensor: "SubtensorInterface", json_output: bool = False
1433
+ ) -> Optional[Balance]:
1338
1434
  """View locking cost of creating a new subnetwork"""
1339
1435
  with console.status(
1340
1436
  f":satellite:Retrieving lock cost from {subtensor.network}...",
@@ -1342,26 +1438,47 @@ async def burn_cost(subtensor: "SubtensorInterface") -> Optional[Balance]:
1342
1438
  ):
1343
1439
  current_burn_cost = await subtensor.burn_cost()
1344
1440
  if current_burn_cost:
1345
- console.print(
1346
- f"Subnet burn cost: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{current_burn_cost}"
1347
- )
1441
+ if json_output:
1442
+ json_console.print(
1443
+ json.dumps({"burn_cost": current_burn_cost.to_dict(), "error": ""})
1444
+ )
1445
+ else:
1446
+ console.print(
1447
+ f"Subnet burn cost: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{current_burn_cost}"
1448
+ )
1348
1449
  return current_burn_cost
1349
1450
  else:
1350
- err_console.print(
1351
- "Subnet burn cost: [red]Failed to get subnet burn cost[/red]"
1352
- )
1451
+ if json_output:
1452
+ json_console.print(
1453
+ json.dumps(
1454
+ {"burn_cost": None, "error": "Failed to get subnet burn cost"}
1455
+ )
1456
+ )
1457
+ else:
1458
+ err_console.print(
1459
+ "Subnet burn cost: [red]Failed to get subnet burn cost[/red]"
1460
+ )
1353
1461
  return None
1354
1462
 
1355
1463
 
1356
1464
  async def create(
1357
- wallet: Wallet, subtensor: "SubtensorInterface", subnet_identity: dict, prompt: bool
1465
+ wallet: Wallet,
1466
+ subtensor: "SubtensorInterface",
1467
+ subnet_identity: dict,
1468
+ json_output: bool,
1469
+ prompt: bool,
1358
1470
  ):
1359
1471
  """Register a subnetwork"""
1360
1472
 
1361
1473
  # Call register command.
1362
- success = await register_subnetwork_extrinsic(
1474
+ success, netuid = await register_subnetwork_extrinsic(
1363
1475
  subtensor, wallet, subnet_identity, prompt=prompt
1364
1476
  )
1477
+ if json_output:
1478
+ # technically, netuid can be `None`, but only if not wait for finalization/inclusion. However, as of present
1479
+ # (2025/04/03), we always use the default `wait_for_finalization=True`, so it will always have a netuid.
1480
+ json_console.print(json.dumps({"success": success, "netuid": netuid}))
1481
+ return success
1365
1482
  if success and prompt:
1366
1483
  # Prompt for user to set identity.
1367
1484
  do_set_identity = Confirm.ask(
@@ -1436,7 +1553,12 @@ async def pow_register(
1436
1553
 
1437
1554
 
1438
1555
  async def register(
1439
- wallet: Wallet, subtensor: "SubtensorInterface", netuid: int, prompt: bool
1556
+ wallet: Wallet,
1557
+ subtensor: "SubtensorInterface",
1558
+ netuid: int,
1559
+ era: Optional[int],
1560
+ json_output: bool,
1561
+ prompt: bool,
1440
1562
  ):
1441
1563
  """Register neuron by recycling some TAO."""
1442
1564
 
@@ -1445,6 +1567,12 @@ async def register(
1445
1567
  block_hash = await subtensor.substrate.get_chain_head()
1446
1568
  if not await subtensor.subnet_exists(netuid=netuid, block_hash=block_hash):
1447
1569
  err_console.print(f"[red]Subnet {netuid} does not exist[/red]")
1570
+ if json_output:
1571
+ json_console.print(
1572
+ json.dumps(
1573
+ {"success": False, "error": f"Subnet {netuid} does not exist"}
1574
+ )
1575
+ )
1448
1576
  return
1449
1577
 
1450
1578
  # Check current recycle amount
@@ -1466,7 +1594,7 @@ async def register(
1466
1594
  )
1467
1595
  return
1468
1596
 
1469
- if prompt:
1597
+ if prompt and not json_output:
1470
1598
  # TODO make this a reusable function, also used in subnets list
1471
1599
  # Show creation table.
1472
1600
  table = Table(
@@ -1518,23 +1646,28 @@ async def register(
1518
1646
  console.print(table)
1519
1647
  if not (
1520
1648
  Confirm.ask(
1521
- f"Your balance is: [{COLOR_PALETTE['GENERAL']['BALANCE']}]{balance}[/{COLOR_PALETTE['GENERAL']['BALANCE']}]\nThe cost to register by recycle is "
1522
- f"[{COLOR_PALETTE['GENERAL']['COST']}]{current_recycle}[/{COLOR_PALETTE['GENERAL']['COST']}]\nDo you want to continue?",
1649
+ f"Your balance is: [{COLOR_PALETTE.G.BAL}]{balance}[/{COLOR_PALETTE.G.BAL}]\n"
1650
+ f"The cost to register by recycle is "
1651
+ f"[{COLOR_PALETTE.G.COST}]{current_recycle}[/{COLOR_PALETTE.G.COST}]\n"
1652
+ f"Do you want to continue?",
1523
1653
  default=False,
1524
1654
  )
1525
1655
  ):
1526
1656
  return
1527
1657
 
1528
1658
  if netuid == 0:
1529
- await root_register_extrinsic(subtensor, wallet=wallet)
1659
+ success, msg = await root_register_extrinsic(subtensor, wallet=wallet)
1530
1660
  else:
1531
- await burned_register_extrinsic(
1661
+ success, msg = await burned_register_extrinsic(
1532
1662
  subtensor,
1533
1663
  wallet=wallet,
1534
1664
  netuid=netuid,
1535
1665
  prompt=False,
1536
1666
  old_balance=balance,
1667
+ era=era,
1537
1668
  )
1669
+ if json_output:
1670
+ json_console.print(json.dumps({"success": success, "msg": msg}))
1538
1671
 
1539
1672
 
1540
1673
  # TODO: Confirm emissions, incentive, Dividends are to be fetched from subnet_state or keep NeuronInfo
@@ -2126,10 +2259,15 @@ async def set_identity(
2126
2259
  return True
2127
2260
 
2128
2261
 
2129
- async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str = None):
2262
+ async def get_identity(
2263
+ subtensor: "SubtensorInterface",
2264
+ netuid: int,
2265
+ title: str = None,
2266
+ json_output: bool = False,
2267
+ ) -> Optional[dict]:
2130
2268
  """Fetch and display existing subnet identity information."""
2131
2269
  if not title:
2132
- title = "Subnet Identity"
2270
+ title = f"Current Subnet {netuid} Identity"
2133
2271
 
2134
2272
  if not await subtensor.subnet_exists(netuid):
2135
2273
  print_error(f"Subnet {netuid} does not exist.")
@@ -2147,10 +2285,12 @@ async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str
2147
2285
  f" for subnet [blue]{netuid}[/blue]"
2148
2286
  f" on {subtensor}"
2149
2287
  )
2288
+ if json_output:
2289
+ json_console.print("{}")
2150
2290
  return {}
2151
-
2152
- if identity:
2153
- table = create_identity_table(title=f"Current Subnet {netuid} Identity")
2291
+ else:
2292
+ table = create_identity_table(title=title)
2293
+ dict_out = {}
2154
2294
  table.add_row("Netuid", str(netuid))
2155
2295
  for key in [
2156
2296
  "subnet_name",
@@ -2163,5 +2303,118 @@ async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str
2163
2303
  ]:
2164
2304
  value = getattr(identity, key, None)
2165
2305
  table.add_row(key, str(value) if value else "~")
2166
- console.print(table)
2306
+ dict_out[key] = value
2307
+ if json_output:
2308
+ json_console.print(json.dumps(dict_out))
2309
+ else:
2310
+ console.print(table)
2167
2311
  return identity
2312
+
2313
+
2314
+ async def get_start_schedule(
2315
+ subtensor: "SubtensorInterface",
2316
+ netuid: int,
2317
+ ) -> None:
2318
+ """Fetch and display existing emission schedule information."""
2319
+
2320
+ if not await subtensor.subnet_exists(netuid):
2321
+ print_error(f"Subnet {netuid} does not exist.")
2322
+ return None
2323
+
2324
+ registration_block = await subtensor.query(
2325
+ module="SubtensorModule",
2326
+ storage_function="NetworkRegisteredAt",
2327
+ params=[netuid],
2328
+ )
2329
+ min_blocks_to_start = Constants.emission_start_schedule
2330
+ current_block = await subtensor.substrate.get_block_number()
2331
+
2332
+ potential_start_block = registration_block + min_blocks_to_start
2333
+ if current_block < potential_start_block:
2334
+ blocks_to_wait = potential_start_block - current_block
2335
+ time_to_wait = blocks_to_duration(blocks_to_wait)
2336
+
2337
+ console.print(
2338
+ f"[blue]Subnet {netuid}[/blue]:\n"
2339
+ f"[blue]Registered at:[/blue] {registration_block}\n"
2340
+ f"[blue]Minimum start block:[/blue] {potential_start_block}\n"
2341
+ f"[blue]Current block:[/blue] {current_block}\n"
2342
+ f"[blue]Blocks remaining:[/blue] {blocks_to_wait}\n"
2343
+ f"[blue]Time to wait:[/blue] {time_to_wait}"
2344
+ )
2345
+ else:
2346
+ console.print(
2347
+ f"[blue]Subnet {netuid}[/blue]:\n"
2348
+ f"[blue]Registered at:[/blue] {registration_block}\n"
2349
+ f"[blue]Current block:[/blue] {current_block}\n"
2350
+ f"[blue]Minimum start block:[/blue] {potential_start_block}\n"
2351
+ f"[dark_sea_green3]Emission schedule can be started[/dark_sea_green3]"
2352
+ )
2353
+
2354
+ return
2355
+
2356
+
2357
+ async def start_subnet(
2358
+ wallet: "Wallet",
2359
+ subtensor: "SubtensorInterface",
2360
+ netuid: int,
2361
+ prompt: bool = False,
2362
+ ) -> bool:
2363
+ """Start a subnet's emission schedule"""
2364
+
2365
+ if not await subtensor.subnet_exists(netuid):
2366
+ print_error(f"Subnet {netuid} does not exist.")
2367
+ return False
2368
+
2369
+ subnet_owner = await subtensor.query(
2370
+ module="SubtensorModule",
2371
+ storage_function="SubnetOwner",
2372
+ params=[netuid],
2373
+ )
2374
+ if subnet_owner != wallet.coldkeypub.ss58_address:
2375
+ print_error(":cross_mark: This wallet doesn't own the specified subnet.")
2376
+ return False
2377
+
2378
+ if prompt:
2379
+ if not Confirm.ask(
2380
+ f"Are you sure you want to start subnet {netuid}'s emission schedule?"
2381
+ ):
2382
+ return False
2383
+
2384
+ if not unlock_key(wallet).success:
2385
+ return False
2386
+
2387
+ with console.status(
2388
+ f":satellite: Starting subnet {netuid}'s emission schedule...", spinner="earth"
2389
+ ):
2390
+ start_call = await subtensor.substrate.compose_call(
2391
+ call_module="SubtensorModule",
2392
+ call_function="start_call",
2393
+ call_params={"netuid": netuid},
2394
+ )
2395
+
2396
+ signed_ext = await subtensor.substrate.create_signed_extrinsic(
2397
+ call=start_call,
2398
+ keypair=wallet.coldkey,
2399
+ )
2400
+
2401
+ response = await subtensor.substrate.submit_extrinsic(
2402
+ extrinsic=signed_ext,
2403
+ wait_for_inclusion=True,
2404
+ wait_for_finalization=True,
2405
+ )
2406
+
2407
+ if await response.is_success:
2408
+ console.print(
2409
+ f":white_heavy_check_mark: [green]Successfully started subnet {netuid}'s emission schedule.[/green]"
2410
+ )
2411
+ return True
2412
+ else:
2413
+ error_msg = format_error_message(await response.error_message)
2414
+ if "FirstEmissionBlockNumberAlreadySet" in error_msg:
2415
+ console.print(f"[dark_sea_green3]Subnet {netuid} already has an emission schedule.[/dark_sea_green3]")
2416
+ return True
2417
+
2418
+ await get_start_schedule(subtensor, netuid)
2419
+ print_error(f":cross_mark: Failed to start subnet: {error_msg}")
2420
+ return False