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.
- bittensor_cli/cli.py +601 -133
- bittensor_cli/src/__init__.py +444 -465
- bittensor_cli/src/bittensor/chain_data.py +6 -2
- bittensor_cli/src/bittensor/extrinsics/registration.py +47 -23
- bittensor_cli/src/bittensor/extrinsics/root.py +10 -11
- bittensor_cli/src/bittensor/extrinsics/transfer.py +5 -3
- bittensor_cli/src/bittensor/subtensor_interface.py +59 -5
- bittensor_cli/src/bittensor/utils.py +6 -2
- bittensor_cli/src/commands/stake/add.py +127 -98
- bittensor_cli/src/commands/stake/children_hotkeys.py +120 -79
- bittensor_cli/src/commands/stake/list.py +54 -20
- bittensor_cli/src/commands/stake/move.py +15 -12
- bittensor_cli/src/commands/stake/remove.py +101 -80
- bittensor_cli/src/commands/subnets/price.py +11 -9
- bittensor_cli/src/commands/subnets/subnets.py +334 -81
- bittensor_cli/src/commands/sudo.py +76 -22
- bittensor_cli/src/commands/wallets.py +656 -40
- bittensor_cli/src/commands/weights.py +21 -11
- bittensor_cli/version.py +14 -11
- {bittensor_cli-9.2.0.dist-info → bittensor_cli-9.4.0.dist-info}/METADATA +8 -9
- bittensor_cli-9.4.0.dist-info/RECORD +35 -0
- {bittensor_cli-9.2.0.dist-info → bittensor_cli-9.4.0.dist-info}/WHEEL +1 -1
- bittensor_cli-9.2.0.dist-info/RECORD +0 -35
- {bittensor_cli-9.2.0.dist-info → bittensor_cli-9.4.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.2.0.dist-info → bittensor_cli-9.4.0.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
214
|
-
|
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
|
219
|
+
root_subnet = next(s for s in subnets_ if s.netuid == 0)
|
218
220
|
other_subnets = sorted(
|
219
|
-
[s for s in
|
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,
|
226
|
+
return sorted_subnets, block_number_
|
225
227
|
|
226
228
|
def calculate_emission_stats(
|
227
|
-
|
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
|
233
|
+
subnet.tao_in.tao for subnet in subnets_ if subnet.netuid != 0
|
232
234
|
)
|
233
|
-
emission_percentage = (total_tao_emitted /
|
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(
|
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}/{
|
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
|
-
|
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
|
-
|
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
|
-
|
272
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
314
|
+
return defined_table
|
313
315
|
|
314
316
|
# Non-live mode
|
315
|
-
def
|
317
|
+
def _create_table(subnets_, block_number_):
|
316
318
|
rows = []
|
317
|
-
_, percentage_string = calculate_emission_stats(
|
319
|
+
_, percentage_string = calculate_emission_stats(subnets_, block_number_)
|
318
320
|
|
319
|
-
for subnet in
|
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
|
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(
|
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
|
407
|
+
sum(float(subnet.price.tao) for subnet in subnets_ if subnet.netuid != 0), 4
|
404
408
|
)
|
405
|
-
total_netuids = len(
|
406
|
-
|
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
|
-
|
412
|
-
return
|
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(
|
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(
|
574
|
+
_, percentage_string = calculate_emission_stats(subnets_, block_number_)
|
520
575
|
|
521
|
-
for subnet in
|
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 =
|
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(
|
710
|
+
total_netuids = len(subnets_)
|
656
711
|
_total_emissions = sum(
|
657
|
-
subnet.tao_in_emission.tao for subnet in
|
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
|
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
|
-
|
737
|
-
|
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
|
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(
|
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
|
-
|
1346
|
-
|
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
|
-
|
1351
|
-
|
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,
|
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,
|
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
|
1522
|
-
f"
|
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(
|
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
|
-
|
2153
|
-
|
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
|
-
|
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
|