meshtensor-cli 9.18.1__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 (74) hide show
  1. meshtensor_cli/__init__.py +22 -0
  2. meshtensor_cli/cli.py +10742 -0
  3. meshtensor_cli/doc_generation_helper.py +4 -0
  4. meshtensor_cli/src/__init__.py +1085 -0
  5. meshtensor_cli/src/commands/__init__.py +0 -0
  6. meshtensor_cli/src/commands/axon/__init__.py +0 -0
  7. meshtensor_cli/src/commands/axon/axon.py +132 -0
  8. meshtensor_cli/src/commands/crowd/__init__.py +0 -0
  9. meshtensor_cli/src/commands/crowd/contribute.py +621 -0
  10. meshtensor_cli/src/commands/crowd/contributors.py +200 -0
  11. meshtensor_cli/src/commands/crowd/create.py +783 -0
  12. meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
  13. meshtensor_cli/src/commands/crowd/refund.py +233 -0
  14. meshtensor_cli/src/commands/crowd/update.py +418 -0
  15. meshtensor_cli/src/commands/crowd/utils.py +124 -0
  16. meshtensor_cli/src/commands/crowd/view.py +991 -0
  17. meshtensor_cli/src/commands/governance/__init__.py +0 -0
  18. meshtensor_cli/src/commands/governance/governance.py +794 -0
  19. meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
  20. meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
  21. meshtensor_cli/src/commands/liquidity/utils.py +202 -0
  22. meshtensor_cli/src/commands/proxy.py +700 -0
  23. meshtensor_cli/src/commands/stake/__init__.py +0 -0
  24. meshtensor_cli/src/commands/stake/add.py +799 -0
  25. meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
  26. meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
  27. meshtensor_cli/src/commands/stake/claim.py +770 -0
  28. meshtensor_cli/src/commands/stake/list.py +738 -0
  29. meshtensor_cli/src/commands/stake/move.py +1211 -0
  30. meshtensor_cli/src/commands/stake/remove.py +1466 -0
  31. meshtensor_cli/src/commands/stake/wizard.py +323 -0
  32. meshtensor_cli/src/commands/subnets/__init__.py +0 -0
  33. meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
  34. meshtensor_cli/src/commands/subnets/price.py +733 -0
  35. meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
  36. meshtensor_cli/src/commands/sudo.py +1294 -0
  37. meshtensor_cli/src/commands/tc/__init__.py +0 -0
  38. meshtensor_cli/src/commands/tc/tc.py +190 -0
  39. meshtensor_cli/src/commands/treasury/__init__.py +0 -0
  40. meshtensor_cli/src/commands/treasury/treasury.py +194 -0
  41. meshtensor_cli/src/commands/view.py +354 -0
  42. meshtensor_cli/src/commands/wallets.py +2311 -0
  43. meshtensor_cli/src/commands/weights.py +467 -0
  44. meshtensor_cli/src/meshtensor/__init__.py +0 -0
  45. meshtensor_cli/src/meshtensor/balances.py +313 -0
  46. meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
  47. meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
  48. meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
  49. meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
  50. meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
  51. meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
  52. meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
  53. meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
  54. meshtensor_cli/src/meshtensor/minigraph.py +254 -0
  55. meshtensor_cli/src/meshtensor/networking.py +12 -0
  56. meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
  57. meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
  58. meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
  59. meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
  60. meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
  61. meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
  62. meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
  63. meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
  64. meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
  65. meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
  66. meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
  67. meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
  68. meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
  69. meshtensor_cli/src/meshtensor/utils.py +2007 -0
  70. meshtensor_cli/version.py +23 -0
  71. meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
  72. meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
  73. meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
  74. meshtensor_cli-9.18.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,515 @@
1
+ import asyncio
2
+ import math
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ from meshtensor_wallet import Wallet
6
+ from rich.prompt import Prompt
7
+ from rich.table import Column, Table
8
+ from rich import box
9
+
10
+ from meshtensor_cli.src import COLOR_PALETTE
11
+ from meshtensor_cli.src.commands import sudo
12
+ from meshtensor_cli.src.meshtensor.utils import (
13
+ confirm_action,
14
+ console,
15
+ print_error,
16
+ json_console,
17
+ U16_MAX,
18
+ print_extrinsic_id,
19
+ print_success,
20
+ )
21
+
22
+ if TYPE_CHECKING:
23
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
24
+
25
+
26
+ async def count(
27
+ meshtensor: "MeshtensorInterface",
28
+ netuid: int,
29
+ json_output: bool = False,
30
+ ) -> Optional[int]:
31
+ """Display how many mechanisms exist for the provided subnet."""
32
+
33
+ block_hash = await meshtensor.substrate.get_chain_head()
34
+ if not await meshtensor.subnet_exists(netuid=netuid, block_hash=block_hash):
35
+ print_error(f"Subnet {netuid} does not exist")
36
+ if json_output:
37
+ json_console.print_json(
38
+ data={"success": False, "error": f"Subnet {netuid} does not exist"}
39
+ )
40
+ return None
41
+
42
+ with console.status(
43
+ f":satellite:Retrieving mechanism count from {meshtensor.network}...",
44
+ spinner="aesthetic",
45
+ ):
46
+ mechanism_count = await meshtensor.get_subnet_mechanisms(
47
+ netuid, block_hash=block_hash
48
+ )
49
+ if not mechanism_count:
50
+ if json_output:
51
+ json_console.print_json(
52
+ data={
53
+ "netuid": netuid,
54
+ "count": None,
55
+ "error": "Failed to get mechanism count",
56
+ }
57
+ )
58
+ else:
59
+ print_error(
60
+ "Subnet mechanism count: [red]Failed to get mechanism count[/red]"
61
+ )
62
+ return None
63
+
64
+ if json_output:
65
+ json_console.print_json(
66
+ data={
67
+ "netuid": netuid,
68
+ "count": mechanism_count,
69
+ "error": "",
70
+ }
71
+ )
72
+ else:
73
+ console.print(
74
+ f"[blue]Subnet {netuid}[/blue] currently has [blue]{mechanism_count}[/blue] mechanism"
75
+ f"{'s' if mechanism_count != 1 else ''}."
76
+ f"\n[dim](Tip: 1 mechanism means there are no mechanisms beyond the main subnet)[/dim]"
77
+ )
78
+
79
+ return mechanism_count
80
+
81
+
82
+ async def get_emission_split(
83
+ meshtensor: "MeshtensorInterface",
84
+ netuid: int,
85
+ json_output: bool = False,
86
+ ) -> Optional[dict]:
87
+ """Display the emission split across mechanisms for a subnet."""
88
+
89
+ count_ = await meshtensor.get_subnet_mechanisms(netuid)
90
+ if count_ == 1:
91
+ console.print(
92
+ f"Subnet {netuid} only has the primary mechanism (mechanism 0). No emission split to display."
93
+ )
94
+ if json_output:
95
+ json_console.print_json(
96
+ data={
97
+ "success": False,
98
+ "error": "Subnet only has the primary mechanism (mechanism 0). No emission split to display.",
99
+ }
100
+ )
101
+ return None
102
+
103
+ emission_split = await meshtensor.get_mechanism_emission_split(netuid) or []
104
+
105
+ even_distribution = False
106
+ total_sum = sum(emission_split)
107
+ if total_sum == 0 and count_ > 0:
108
+ even_distribution = True
109
+ base, remainder = divmod(U16_MAX, count_)
110
+ emission_split = [base for _ in range(count_)]
111
+ if remainder:
112
+ emission_split[0] += remainder
113
+ total_sum = sum(emission_split)
114
+
115
+ emission_percentages = (
116
+ [round((value / total_sum) * 100, 6) for value in emission_split]
117
+ if total_sum > 0
118
+ else [0.0 for _ in emission_split]
119
+ )
120
+
121
+ data = {
122
+ "netuid": netuid,
123
+ "raw_count": count_,
124
+ "visible_count": max(count_ - 1, 0),
125
+ "split": emission_split if count_ else [],
126
+ "percentages": emission_percentages if count_ else [],
127
+ "even_distribution": even_distribution,
128
+ }
129
+
130
+ if json_output:
131
+ json_console.print_json(data=data)
132
+ else:
133
+ table = Table(
134
+ Column(
135
+ "[bold white]Mechanism Index[/]",
136
+ justify="center",
137
+ style=COLOR_PALETTE.G.NETUID,
138
+ ),
139
+ Column(
140
+ "[bold white]Weight (u16)[/]",
141
+ justify="right",
142
+ style=COLOR_PALETTE.STAKE.STAKE_ALPHA,
143
+ ),
144
+ Column(
145
+ "[bold white]Share (%)[/]",
146
+ justify="right",
147
+ style=COLOR_PALETTE.POOLS.EMISSION,
148
+ ),
149
+ title=f"\n[{COLOR_PALETTE.G.HEADER}]Subnet {netuid} • Emission split[/]\n"
150
+ f"[{COLOR_PALETTE.G.SUBHEAD}]Network: {meshtensor.network}[/{COLOR_PALETTE.G.SUBHEAD}]",
151
+ box=box.SIMPLE,
152
+ show_footer=True,
153
+ border_style="bright_black",
154
+ )
155
+
156
+ total_weight = sum(emission_split)
157
+ share_percent = (total_weight / U16_MAX) * 100 if U16_MAX else 0
158
+
159
+ for idx, value in enumerate(emission_split):
160
+ share = (
161
+ emission_percentages[idx] if idx < len(emission_percentages) else 0.0
162
+ )
163
+ table.add_row(str(idx), str(value), f"{share:.6f}")
164
+
165
+ table.add_row(
166
+ "[dim]Total[/dim]",
167
+ f"[{COLOR_PALETTE.STAKE.STAKE_ALPHA}]{total_weight}[/{COLOR_PALETTE.STAKE.STAKE_ALPHA}]",
168
+ f"[{COLOR_PALETTE.POOLS.EMISSION}]{share_percent:.6f}[/{COLOR_PALETTE.POOLS.EMISSION}]",
169
+ )
170
+
171
+ console.print(table)
172
+ footer = "[dim]Totals are expressed as a fraction of 65535 (U16_MAX).[/dim]"
173
+ if even_distribution:
174
+ footer += (
175
+ "\n[dim]No custom split found; displaying an even distribution.[/dim]"
176
+ )
177
+ console.print(footer)
178
+
179
+ return data
180
+
181
+
182
+ async def set_emission_split(
183
+ meshtensor: "MeshtensorInterface",
184
+ wallet: Wallet,
185
+ netuid: int,
186
+ new_emission_split: Optional[str],
187
+ proxy: Optional[str],
188
+ wait_for_inclusion: bool,
189
+ wait_for_finalization: bool,
190
+ prompt: bool,
191
+ decline: bool,
192
+ quiet: bool,
193
+ json_output: bool,
194
+ ) -> bool:
195
+ """Set the emission split across mechanisms for a subnet."""
196
+
197
+ mech_count, existing_split = await asyncio.gather(
198
+ meshtensor.get_subnet_mechanisms(netuid),
199
+ meshtensor.get_mechanism_emission_split(netuid),
200
+ )
201
+
202
+ if mech_count == 0:
203
+ message = (
204
+ f"Subnet {netuid} does not currently contain any mechanisms to configure."
205
+ )
206
+ if json_output:
207
+ json_console.print_json(data={"success": False, "error": message})
208
+ else:
209
+ print_error(message)
210
+ return False
211
+
212
+ if not json_output:
213
+ await get_emission_split(
214
+ meshtensor=meshtensor,
215
+ netuid=netuid,
216
+ json_output=False,
217
+ )
218
+
219
+ existing_split = [int(value) for value in existing_split]
220
+ if len(existing_split) < mech_count:
221
+ existing_split.extend([0] * (mech_count - len(existing_split)))
222
+
223
+ if new_emission_split is not None:
224
+ try:
225
+ weights = [
226
+ float(item.strip())
227
+ for item in new_emission_split.split(",")
228
+ if item.strip() != ""
229
+ ]
230
+ except ValueError:
231
+ message = (
232
+ "Invalid `--split` values. Provide a comma-separated list of numbers."
233
+ )
234
+ if json_output:
235
+ json_console.print_json(data={"success": False, "error": message})
236
+ else:
237
+ print_error(message)
238
+ return False
239
+ else:
240
+ if not prompt:
241
+ print_error(
242
+ "Split values not supplied with `--no-prompt` flag. Cannot continue."
243
+ )
244
+ return False
245
+
246
+ weights: list[float] = []
247
+ total_existing = sum(existing_split) or 1
248
+ console.print("\n[dim]You either provide U16 values or percentages.[/dim]")
249
+ for idx in range(mech_count):
250
+ current_value = existing_split[idx]
251
+ current_percent = (
252
+ (current_value / total_existing) * 100 if total_existing else 0
253
+ )
254
+ label = (
255
+ "[blue]Main Mechanism (0)[/blue]"
256
+ if idx == 0
257
+ else f"[blue]Mechanism {idx}[/blue]"
258
+ )
259
+ response = Prompt.ask(
260
+ (
261
+ f"Relative weight for {label} "
262
+ f"[{COLOR_PALETTE.STAKE.STAKE_ALPHA}](current: {current_value} ~ {current_percent:.2f}%)[/{COLOR_PALETTE.STAKE.STAKE_ALPHA}]"
263
+ )
264
+ )
265
+ try:
266
+ weights.append(float(response))
267
+ except ValueError:
268
+ print_error("Invalid number provided. Aborting.")
269
+ return False
270
+
271
+ if len(weights) != mech_count:
272
+ message = f"Expected {mech_count} weight values, received {len(weights)}."
273
+ if json_output:
274
+ json_console.print_json(data={"success": False, "error": message})
275
+ else:
276
+ print_error(message)
277
+ return False
278
+
279
+ if any(value < 0 for value in weights):
280
+ message = "Weights must be non-negative."
281
+ if json_output:
282
+ json_console.print_json(data={"success": False, "error": message})
283
+ else:
284
+ print_error(message)
285
+ return False
286
+
287
+ try:
288
+ normalized_weights, fractions = _normalize_emission_weights(weights)
289
+ except ValueError as exc:
290
+ message = str(exc)
291
+ if json_output:
292
+ json_console.print_json(data={"success": False, "error": message})
293
+ else:
294
+ print_error(message)
295
+ return False
296
+
297
+ if normalized_weights == existing_split:
298
+ message = "[dark_sea_green3]Emission split unchanged.[/dark_sea_green3]"
299
+ if json_output:
300
+ json_console.print_json(
301
+ data={
302
+ "success": True,
303
+ "message": "Emission split unchanged.",
304
+ "split": normalized_weights,
305
+ "percentages": [round(value * 100, 6) for value in fractions],
306
+ "extrinsic_identifier": None,
307
+ }
308
+ )
309
+ else:
310
+ print_success(message)
311
+ return True
312
+
313
+ if not json_output:
314
+ table = Table(
315
+ Column(
316
+ "[bold white]Mechanism Index[/]",
317
+ justify="center",
318
+ style=COLOR_PALETTE.G.NETUID,
319
+ ),
320
+ Column(
321
+ "[bold white]Weight (u16)[/]",
322
+ justify="right",
323
+ style=COLOR_PALETTE.STAKE.STAKE_ALPHA,
324
+ ),
325
+ Column(
326
+ "[bold white]Share (%)[/]",
327
+ justify="right",
328
+ style=COLOR_PALETTE.POOLS.EMISSION,
329
+ ),
330
+ title=(
331
+ f"\n[{COLOR_PALETTE.G.HEADER}]Proposed emission split[/{COLOR_PALETTE.G.HEADER}]\n"
332
+ f"[{COLOR_PALETTE.G.SUBHEAD}]Subnet {netuid}[/{COLOR_PALETTE.G.SUBHEAD}]"
333
+ ),
334
+ box=box.SIMPLE,
335
+ show_footer=True,
336
+ border_style="bright_black",
337
+ )
338
+
339
+ total_weight = sum(normalized_weights)
340
+ total_share_percent = (total_weight / U16_MAX) * 100 if U16_MAX else 0
341
+
342
+ for idx, weight in enumerate(normalized_weights):
343
+ share_percent = fractions[idx] * 100 if idx < len(fractions) else 0.0
344
+ table.add_row(str(idx), str(weight), f"{share_percent:.6f}")
345
+
346
+ table.add_row("", "", "", style="dim")
347
+ table.add_row(
348
+ "[dim]Total[/dim]",
349
+ f"[{COLOR_PALETTE.STAKE.STAKE_ALPHA}]{total_weight}[/{COLOR_PALETTE.STAKE.STAKE_ALPHA}]",
350
+ f"[{COLOR_PALETTE.POOLS.EMISSION}]{total_share_percent:.6f}[/{COLOR_PALETTE.POOLS.EMISSION}]",
351
+ )
352
+
353
+ console.print(table)
354
+
355
+ if not confirm_action(
356
+ "Proceed with these emission weights?",
357
+ default=True,
358
+ decline=decline,
359
+ quiet=quiet,
360
+ ):
361
+ print_error("Aborted!")
362
+ return False
363
+
364
+ success, err_msg, ext_id = await set_mechanism_emission(
365
+ wallet=wallet,
366
+ meshtensor=meshtensor,
367
+ netuid=netuid,
368
+ split=normalized_weights,
369
+ proxy=proxy,
370
+ wait_for_inclusion=wait_for_inclusion,
371
+ wait_for_finalization=wait_for_finalization,
372
+ json_output=json_output,
373
+ )
374
+
375
+ if json_output:
376
+ json_console.print_json(
377
+ data={
378
+ "success": success,
379
+ "err_msg": err_msg,
380
+ "split": normalized_weights,
381
+ "percentages": [round(value * 100, 6) for value in fractions],
382
+ "extrinsic_identifier": ext_id,
383
+ }
384
+ )
385
+
386
+ return success
387
+
388
+
389
+ def _normalize_emission_weights(values: list[float]) -> tuple[list[int], list[float]]:
390
+ total = sum(values)
391
+ if total <= 0:
392
+ raise ValueError("Sum of emission weights must be greater than zero.")
393
+
394
+ fractions = [value / total for value in values]
395
+ scaled = [fraction * U16_MAX for fraction in fractions]
396
+ base = [math.floor(value) for value in scaled]
397
+ remainder = int(U16_MAX - sum(base))
398
+
399
+ if remainder > 0:
400
+ fractional_parts = [value - math.floor(value) for value in scaled]
401
+ order = sorted(
402
+ range(len(base)), key=lambda idx_: fractional_parts[idx_], reverse=True
403
+ )
404
+ idx = 0
405
+ length = len(order)
406
+ while remainder > 0 and length > 0:
407
+ base[order[idx % length]] += 1
408
+ remainder -= 1
409
+ idx += 1
410
+
411
+ return [int(value) for value in base], fractions
412
+
413
+
414
+ async def set_mechanism_count(
415
+ wallet: Wallet,
416
+ meshtensor: "MeshtensorInterface",
417
+ netuid: int,
418
+ mechanism_count: int,
419
+ previous_count: int,
420
+ proxy: Optional[str],
421
+ wait_for_inclusion: bool,
422
+ wait_for_finalization: bool,
423
+ decline: bool,
424
+ quiet: bool,
425
+ json_output: bool,
426
+ ) -> tuple[bool, str, Optional[str]]:
427
+ """Set the number of mechanisms for a subnet."""
428
+
429
+ if mechanism_count < 1:
430
+ err_msg = "Mechanism count must be greater than or equal to one."
431
+ if not json_output:
432
+ print_error(err_msg)
433
+ return False, err_msg, None
434
+
435
+ if not await meshtensor.subnet_exists(netuid):
436
+ err_msg = f"Subnet with netuid {netuid} does not exist."
437
+ if not json_output:
438
+ print_error(err_msg)
439
+ return False, err_msg, None
440
+
441
+ if not confirm_action(
442
+ f"Subnet [blue]{netuid}[/blue] currently has [blue]{previous_count}[/blue] mechanism"
443
+ f"{'s' if previous_count != 1 else ''}."
444
+ f" Set it to [blue]{mechanism_count}[/blue]?",
445
+ decline=decline,
446
+ quiet=quiet,
447
+ ):
448
+ return False, "User cancelled", None
449
+
450
+ success, err_msg, ext_receipt = await sudo.set_mechanism_count_extrinsic(
451
+ meshtensor=meshtensor,
452
+ wallet=wallet,
453
+ netuid=netuid,
454
+ mech_count=mechanism_count,
455
+ proxy=proxy,
456
+ wait_for_inclusion=wait_for_inclusion,
457
+ wait_for_finalization=wait_for_finalization,
458
+ )
459
+ ext_id = await ext_receipt.get_extrinsic_identifier() if success else None
460
+
461
+ if json_output:
462
+ return success, err_msg, ext_id
463
+
464
+ if success:
465
+ await print_extrinsic_id(ext_receipt)
466
+ print_success(
467
+ f"[dark_sea_green3]Mechanism count set to {mechanism_count} for subnet {netuid}[/dark_sea_green3]"
468
+ )
469
+ else:
470
+ print_error(f"Failed: {err_msg}")
471
+
472
+ return success, err_msg, ext_id
473
+
474
+
475
+ async def set_mechanism_emission(
476
+ wallet: Wallet,
477
+ meshtensor: "MeshtensorInterface",
478
+ netuid: int,
479
+ split: list[int],
480
+ proxy: Optional[str],
481
+ wait_for_inclusion: bool,
482
+ wait_for_finalization: bool,
483
+ json_output: bool,
484
+ ) -> tuple[bool, str, Optional[str]]:
485
+ """Set the emission split for mechanisms within a subnet."""
486
+
487
+ if not split:
488
+ err_msg = "Emission split must include at least one weight."
489
+ if not json_output:
490
+ print_error(err_msg)
491
+ return False, err_msg, None
492
+
493
+ success, err_msg, ext_receipt = await sudo.set_mechanism_emission_extrinsic(
494
+ meshtensor=meshtensor,
495
+ wallet=wallet,
496
+ netuid=netuid,
497
+ split=split,
498
+ wait_for_inclusion=wait_for_inclusion,
499
+ wait_for_finalization=wait_for_finalization,
500
+ proxy=proxy,
501
+ )
502
+ ext_id = await ext_receipt.get_extrinsic_identifier() if success else None
503
+
504
+ if json_output:
505
+ return success, err_msg, ext_id
506
+
507
+ if success:
508
+ await print_extrinsic_id(ext_receipt)
509
+ print_success(
510
+ f"[dark_sea_green3]Emission split updated for subnet {netuid}[/dark_sea_green3]"
511
+ )
512
+ else:
513
+ print_error(f"Failed: {err_msg}")
514
+
515
+ return success, err_msg, ext_id