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,865 @@
1
+ import asyncio
2
+ import json
3
+ from typing import Optional
4
+
5
+ from meshtensor_wallet import Wallet
6
+ from rich.prompt import IntPrompt, FloatPrompt
7
+ from rich.table import Table
8
+ from rich.text import Text
9
+ from async_substrate_interface.errors import SubstrateRequestException
10
+
11
+ from meshtensor_cli.src.meshtensor.balances import Balance
12
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
13
+ from meshtensor_cli.src.meshtensor.utils import (
14
+ confirm_action,
15
+ console,
16
+ print_error,
17
+ float_to_u16,
18
+ float_to_u64,
19
+ print_success,
20
+ u16_to_float,
21
+ u64_to_float,
22
+ is_valid_ss58_address,
23
+ format_error_message,
24
+ unlock_key,
25
+ json_console,
26
+ get_hotkey_pub_ss58,
27
+ print_extrinsic_id,
28
+ )
29
+
30
+
31
+ async def get_childkey_completion_block(
32
+ meshtensor: MeshtensorInterface, netuid: int
33
+ ) -> tuple[int, int]:
34
+ """
35
+ Calculates the block at which the childkey set request will complete
36
+ """
37
+ bh = await meshtensor.substrate.get_chain_head()
38
+ blocks_since_last_step_query = meshtensor.query(
39
+ "MeshtensorModule", "BlocksSinceLastStep", params=[netuid], block_hash=bh
40
+ )
41
+ tempo_query = meshtensor.get_hyperparameter(
42
+ param_name="Tempo", netuid=netuid, block_hash=bh
43
+ )
44
+ block_number, blocks_since_last_step, tempo = await asyncio.gather(
45
+ meshtensor.substrate.get_block_number(block_hash=bh),
46
+ blocks_since_last_step_query,
47
+ tempo_query,
48
+ )
49
+ cooldown = block_number + 7200
50
+ blocks_left_in_tempo = tempo - blocks_since_last_step
51
+ next_tempo = block_number + blocks_left_in_tempo
52
+ next_epoch_after_cooldown = (cooldown - next_tempo) % (tempo + 1) + cooldown
53
+ return block_number, next_epoch_after_cooldown
54
+
55
+
56
+ async def set_children_extrinsic(
57
+ meshtensor: "MeshtensorInterface",
58
+ wallet: Wallet,
59
+ hotkey: str,
60
+ netuid: int,
61
+ proxy: Optional[str],
62
+ children_with_proportions: list[tuple[float, str]],
63
+ wait_for_inclusion: bool = True,
64
+ wait_for_finalization: bool = False,
65
+ prompt: bool = False,
66
+ decline: bool = False,
67
+ quiet: bool = False,
68
+ ) -> tuple[bool, str, Optional[str]]:
69
+ """
70
+ Sets children hotkeys with proportions assigned from the parent.
71
+
72
+ :param: meshtensor: Meshtensor endpoint to use.
73
+ :param: wallet: Meshtensor wallet object.
74
+ :param: hotkey: Parent hotkey.
75
+ :param: children_with_proportions: Children hotkeys.
76
+ :param: netuid: Unique identifier of for the subnet.
77
+ :param: wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
78
+ `False` if the extrinsic fails to enter the block within the timeout.
79
+ :param: wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `
80
+ `True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
81
+ :param: prompt: If `True`, the call waits for confirmation from the user before proceeding.
82
+
83
+ :return: A tuple containing a success flag, an optional error message, and the extrinsic identifier
84
+ """
85
+ # Check if all children are being revoked
86
+ all_revoked = len(children_with_proportions) == 0
87
+
88
+ operation = "Revoking all child hotkeys" if all_revoked else "Setting child hotkeys"
89
+
90
+ # Ask before moving on.
91
+ if prompt:
92
+ if all_revoked:
93
+ if not confirm_action(
94
+ f"Do you want to revoke all children hotkeys for hotkey {hotkey} on netuid {netuid}?",
95
+ decline=decline,
96
+ quiet=quiet,
97
+ ):
98
+ return False, "Operation Cancelled", None
99
+ else:
100
+ if not confirm_action(
101
+ "Do you want to set children hotkeys:\n[bold white]{}[/bold white]?".format(
102
+ "\n".join(
103
+ f" {child[1]}: {child[0]}"
104
+ for child in children_with_proportions
105
+ )
106
+ ),
107
+ decline=decline,
108
+ quiet=quiet,
109
+ ):
110
+ return False, "Operation Cancelled", None
111
+
112
+ # Decrypt coldkey.
113
+ if not (unlock_status := unlock_key(wallet, print_out=False)).success:
114
+ return False, unlock_status.message, ""
115
+
116
+ with console.status(
117
+ f":satellite: {operation} on [white]{meshtensor.network}[/white] ..."
118
+ ):
119
+ if not all_revoked:
120
+ normalized_children = prepare_child_proportions(children_with_proportions)
121
+ else:
122
+ normalized_children = []
123
+
124
+ call = await meshtensor.substrate.compose_call(
125
+ call_module="MeshtensorModule",
126
+ call_function="set_children",
127
+ call_params={
128
+ "hotkey": hotkey,
129
+ "children": normalized_children,
130
+ "netuid": netuid,
131
+ },
132
+ )
133
+ success, error_message, ext_receipt = await meshtensor.sign_and_send_extrinsic(
134
+ call, wallet, wait_for_inclusion, wait_for_finalization, proxy=proxy
135
+ )
136
+
137
+ if not wait_for_finalization and not wait_for_inclusion:
138
+ return (
139
+ True,
140
+ f"Not waiting for finalization or inclusion. {operation} initiated.",
141
+ None,
142
+ )
143
+
144
+ if success:
145
+ ext_id = await ext_receipt.get_extrinsic_identifier()
146
+ await print_extrinsic_id(ext_receipt)
147
+ modifier = "included"
148
+ if wait_for_finalization:
149
+ print_success("Finalized")
150
+ modifier = "finalized"
151
+ return True, f"{operation} successfully {modifier}.", ext_id
152
+ else:
153
+ print_error(f"Failed: {error_message}")
154
+ return False, error_message, None
155
+
156
+
157
+ async def set_childkey_take_extrinsic(
158
+ meshtensor: "MeshtensorInterface",
159
+ wallet: Wallet,
160
+ hotkey: str,
161
+ netuid: int,
162
+ take: float,
163
+ proxy: Optional[str] = None,
164
+ wait_for_inclusion: bool = True,
165
+ wait_for_finalization: bool = False,
166
+ prompt: bool = True,
167
+ decline: bool = False,
168
+ quiet: bool = False,
169
+ ) -> tuple[bool, str, Optional[str]]:
170
+ """
171
+ Sets childkey take.
172
+
173
+ :param: meshtensor: Meshtensor endpoint to use.
174
+ :param: wallet: Meshtensor wallet object.
175
+ :param: hotkey: Child hotkey.
176
+ :param: take: Childkey Take value.
177
+ :param: netuid: Unique identifier of for the subnet.
178
+ :param: proxy: Optional proxy to use to make this extrinsic submission.
179
+ :param: wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
180
+ `False` if the extrinsic fails to enter the block within the timeout.
181
+ :param: wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `
182
+ `True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
183
+ :param: prompt: If `True`, the call waits for confirmation from the user before proceeding.
184
+
185
+ :return: A tuple containing a success flag, an optional error message, and an optional extrinsic identifier
186
+ """
187
+
188
+ # Ask before moving on.
189
+ if prompt:
190
+ if not confirm_action(
191
+ f"Do you want to set childkey take to: [bold white]{take * 100}%[/bold white]?",
192
+ decline=decline,
193
+ quiet=quiet,
194
+ ):
195
+ return False, "Operation Cancelled", None
196
+
197
+ # Decrypt coldkey.
198
+ if not (unlock_status := unlock_key(wallet, print_out=False)).success:
199
+ return False, unlock_status.message, None
200
+
201
+ with console.status(
202
+ f":satellite: Setting childkey take on [white]{meshtensor.network}[/white] ..."
203
+ ):
204
+ try:
205
+ if 0 <= take <= 0.18:
206
+ take_u16 = float_to_u16(take)
207
+ else:
208
+ return False, "Invalid take value", None
209
+
210
+ call = await meshtensor.substrate.compose_call(
211
+ call_module="MeshtensorModule",
212
+ call_function="set_childkey_take",
213
+ call_params={
214
+ "hotkey": hotkey,
215
+ "take": take_u16,
216
+ "netuid": netuid,
217
+ },
218
+ )
219
+ (
220
+ success,
221
+ error_message,
222
+ ext_receipt,
223
+ ) = await meshtensor.sign_and_send_extrinsic(
224
+ call, wallet, wait_for_inclusion, wait_for_finalization, proxy=proxy
225
+ )
226
+
227
+ if not wait_for_finalization and not wait_for_inclusion:
228
+ return (
229
+ True,
230
+ "Not waiting for finalization or inclusion. Set childkey take initiated.",
231
+ None,
232
+ )
233
+
234
+ if success:
235
+ ext_id = await ext_receipt.get_extrinsic_identifier()
236
+ await print_extrinsic_id(ext_receipt)
237
+ modifier = "included"
238
+ if wait_for_finalization:
239
+ modifier = "finalized"
240
+ print_success("Finalized")
241
+ return True, f"Successfully {modifier} childkey take", ext_id
242
+ else:
243
+ print_error(f"Failed: {error_message}")
244
+ return False, error_message, None
245
+
246
+ except SubstrateRequestException as e:
247
+ return (
248
+ False,
249
+ f"Exception occurred while setting childkey take: {format_error_message(e)}",
250
+ None,
251
+ )
252
+
253
+
254
+ async def get_childkey_take(meshtensor, hotkey: str, netuid: int) -> Optional[int]:
255
+ """
256
+ Get the childkey take of a hotkey on a specific network.
257
+ Args:
258
+ - hotkey (str): The hotkey to search for.
259
+ - netuid (int): The netuid to search for.
260
+
261
+ Returns:
262
+ - Optional[float]: The value of the "ChildkeyTake" if found, or None if any error occurs.
263
+ """
264
+ try:
265
+ childkey_take_ = await meshtensor.query(
266
+ module="MeshtensorModule",
267
+ storage_function="ChildkeyTake",
268
+ params=[hotkey, netuid],
269
+ )
270
+ if childkey_take_:
271
+ return int(childkey_take_)
272
+
273
+ except SubstrateRequestException as e:
274
+ print_error(f"Error querying ChildKeys: {format_error_message(e)}")
275
+ return None
276
+
277
+
278
+ def prepare_child_proportions(children_with_proportions):
279
+ """
280
+ Convert proportions to u64 and normalize, ensuring total does not exceed u64 max.
281
+ """
282
+ children_u64 = [
283
+ (float_to_u64(proportion), child)
284
+ for proportion, child in children_with_proportions
285
+ ]
286
+ total = sum(proportion for proportion, _ in children_u64)
287
+
288
+ if total > (2**64 - 1):
289
+ excess = total - (2**64 - 1)
290
+ if excess > (2**64 * 0.01): # Example threshold of 1% of u64 max
291
+ raise ValueError("Excess is too great to normalize proportions")
292
+ largest_child_index = max(
293
+ range(len(children_u64)), key=lambda i: children_u64[i][0]
294
+ )
295
+ children_u64[largest_child_index] = (
296
+ children_u64[largest_child_index][0] - excess,
297
+ children_u64[largest_child_index][1],
298
+ )
299
+
300
+ return children_u64
301
+
302
+
303
+ async def get_children(
304
+ wallet: Wallet, meshtensor: "MeshtensorInterface", netuid: Optional[int] = None
305
+ ):
306
+ # TODO meshlet asks separately for the hotkey from the user, should we do this, or the way we do it now?
307
+ """
308
+ Retrieves the child hotkeys for the specified wallet.
309
+
310
+ Args:
311
+ - wallet: The wallet object containing the hotkey information.
312
+ Type: Wallet
313
+ - meshtensor: Interface to interact with the meshtensor network.
314
+ Type: MeshtensorInterface
315
+ - netuid: Optional subnet identifier. If not provided, retrieves data for all subnets.
316
+ Type: Optional[int]
317
+
318
+ Returns:
319
+ - If netuid is specified, returns the list of child hotkeys for the given netuid.
320
+ Type: List[tuple[int, str]]
321
+ - If netuid is not specified, generates and prints a summary table of all child hotkeys across all subnets.
322
+ """
323
+
324
+ async def get_take(child: tuple, netuid__: int) -> float:
325
+ """
326
+ Get the take value for a given meshtensor, hotkey, and netuid.
327
+
328
+ Arguments:
329
+ child: The hotkey to retrieve the take value for.
330
+ netuid__: the netuid to retrieve the take value for.
331
+
332
+ Returns:
333
+ The take value as a float. If the take value is not available, it returns 0.
334
+
335
+ """
336
+ child_hotkey = child[1]
337
+ take_u16 = await get_childkey_take(
338
+ meshtensor=meshtensor, hotkey=child_hotkey, netuid=netuid__
339
+ )
340
+ if take_u16:
341
+ return u16_to_float(take_u16)
342
+ else:
343
+ return 0
344
+
345
+ async def _render_table(
346
+ parent_hotkey: str,
347
+ netuid_children_: list[tuple[int, list[tuple[int, str]]]],
348
+ ):
349
+ """
350
+ Retrieves and renders children hotkeys and their details for a given parent hotkey.
351
+ """
352
+ # Initialize Rich table for pretty printing
353
+ table = Table(
354
+ header_style="bold white",
355
+ border_style="bright_black",
356
+ style="dim",
357
+ )
358
+
359
+ # Add columns to the table with specific styles
360
+ table.add_column("Netuid", style="dark_orange", no_wrap=True, justify="center")
361
+ table.add_column("Child Hotkey", style="bold bright_magenta")
362
+ table.add_column("Proportion", style="bold cyan", no_wrap=True, justify="right")
363
+ table.add_column(
364
+ "Childkey Take", style="light_goldenrod2", no_wrap=True, justify="right"
365
+ )
366
+ table.add_column(
367
+ "Current Stake Weight", style="bold red", no_wrap=True, justify="right"
368
+ )
369
+
370
+ if not netuid_children_:
371
+ console.print(table)
372
+ console.print(
373
+ f"[bold red]There are currently no child hotkeys with parent hotkey: "
374
+ f"{wallet.name} | {wallet.hotkey_str} ({parent_hotkey}).[/bold red]"
375
+ )
376
+ return
377
+
378
+ # calculate totals per subnet
379
+ total_proportion = 0
380
+ total_stake_weight = 0
381
+
382
+ netuid_children_.sort(key=lambda x: x[0]) # Sort by netuid in ascending order
383
+ unique_keys = set(
384
+ [parent_hotkey]
385
+ + [s for _, child_list in netuid_children_ for _, s in child_list]
386
+ )
387
+ hotkey_stake_dict = await meshtensor.get_total_stake_for_hotkey(
388
+ *unique_keys,
389
+ netuids=[n[0] for n in netuid_children_],
390
+ )
391
+ parent_total = sum(hotkey_stake_dict[parent_hotkey].values())
392
+ insert_text = (
393
+ " "
394
+ if netuid is None
395
+ else f" on netuids: {', '.join(str(n[0]) for n in netuid_children_)} "
396
+ )
397
+ console.print(
398
+ f"The total stake of parent hotkey '{parent_hotkey}'{insert_text}is {parent_total}."
399
+ )
400
+
401
+ for index, (child_netuid, children_) in enumerate(netuid_children_):
402
+ # calculate totals
403
+ total_proportion_per_netuid = 0
404
+ total_stake_weight_per_netuid = 0
405
+ avg_take_per_netuid = 0.0
406
+
407
+ hotkey_stake: dict[int, Balance] = hotkey_stake_dict[parent_hotkey]
408
+
409
+ children_info = []
410
+ child_takes = await asyncio.gather(
411
+ *[get_take(c, child_netuid) for c in children_]
412
+ )
413
+ for child, child_take in zip(children_, child_takes):
414
+ proportion = child[0]
415
+ child_hotkey = child[1]
416
+
417
+ # add to totals
418
+ avg_take_per_netuid += child_take
419
+
420
+ converted_proportion = u64_to_float(proportion)
421
+
422
+ children_info.append(
423
+ (
424
+ converted_proportion,
425
+ child_hotkey,
426
+ hotkey_stake_dict[child_hotkey][child_netuid],
427
+ child_take,
428
+ )
429
+ )
430
+
431
+ children_info.sort(
432
+ key=lambda x: x[0], reverse=True
433
+ ) # sorting by proportion (highest first)
434
+
435
+ for proportion_, hotkey, stake, child_take in children_info:
436
+ proportion_percent = proportion_ * 100 # Proportion in percent
437
+ proportion_tao = (
438
+ hotkey_stake[child_netuid].tao * proportion_
439
+ ) # Proportion in MESH
440
+
441
+ total_proportion_per_netuid += proportion_percent
442
+
443
+ # Conditionally format text
444
+ proportion_str = f"{proportion_percent:.3f}% ({proportion_tao:.3f}τ)"
445
+ stake_weight = stake.tao + proportion_tao
446
+ total_stake_weight_per_netuid += stake_weight
447
+ take_str = f"{child_take * 100:.3f}%"
448
+
449
+ hotkey = Text(hotkey, style="italic red" if proportion_ == 0 else "")
450
+ table.add_row(
451
+ str(child_netuid),
452
+ hotkey,
453
+ proportion_str,
454
+ take_str,
455
+ str(f"{stake_weight:.3f}"),
456
+ )
457
+
458
+ avg_take_per_netuid = avg_take_per_netuid / len(children_info)
459
+
460
+ # add totals row for this netuid
461
+ table.add_row(
462
+ "",
463
+ "[dim]Total[/dim]",
464
+ f"[dim]{total_proportion_per_netuid:.3f}%[/dim]",
465
+ f"[dim](avg) {avg_take_per_netuid * 100:.3f}%[/dim]",
466
+ f"[dim]{total_stake_weight_per_netuid:.3f}τ[/dim]",
467
+ style="dim",
468
+ )
469
+
470
+ # add to grand totals
471
+ total_proportion += total_proportion_per_netuid
472
+ total_stake_weight += total_stake_weight_per_netuid
473
+
474
+ # Add a dividing line if there are more than one netuid
475
+ if len(netuid_children_) > 1:
476
+ table.add_section()
477
+
478
+ console.print(table)
479
+
480
+ # Core logic for get_children
481
+ if netuid is None:
482
+ # get all netuids
483
+ netuids = await meshtensor.get_all_subnet_netuids()
484
+ netuid_children_tuples = []
485
+ for netuid_ in netuids:
486
+ success, children, err_mg = await meshtensor.get_children(
487
+ get_hotkey_pub_ss58(wallet), netuid_
488
+ )
489
+ if children:
490
+ netuid_children_tuples.append((netuid_, children))
491
+ if not success:
492
+ print_error(
493
+ f"Failed to get children from meshtensor {netuid_}: {err_mg}"
494
+ )
495
+ await _render_table(get_hotkey_pub_ss58(wallet), netuid_children_tuples)
496
+ else:
497
+ success, children, err_mg = await meshtensor.get_children(
498
+ get_hotkey_pub_ss58(wallet), netuid
499
+ )
500
+ if not success:
501
+ print_error(f"Failed to get children from meshtensor: {err_mg}")
502
+ if children:
503
+ netuid_children_tuples = [(netuid, children)]
504
+ await _render_table(get_hotkey_pub_ss58(wallet), netuid_children_tuples)
505
+
506
+ return children
507
+
508
+
509
+ async def set_children(
510
+ wallet: Wallet,
511
+ meshtensor: "MeshtensorInterface",
512
+ children: list[str],
513
+ proportions: list[float],
514
+ netuid: Optional[int] = None,
515
+ wait_for_inclusion: bool = True,
516
+ wait_for_finalization: bool = True,
517
+ prompt: bool = True,
518
+ json_output: bool = False,
519
+ proxy: Optional[str] = None,
520
+ ):
521
+ """Set children hotkeys."""
522
+ # TODO holy shit I hate this. It needs to be rewritten.
523
+ # Validate children SS58 addresses
524
+ # TODO check to see if this should be allowed to be specified by user instead of pulling from wallet
525
+ hotkey = get_hotkey_pub_ss58(wallet)
526
+ for child in children:
527
+ if not is_valid_ss58_address(child):
528
+ print_error(f"Invalid SS58 address: {child}")
529
+ return
530
+ if child == hotkey:
531
+ print_error("Cannot set yourself as a child.")
532
+ return
533
+
534
+ total_proposed = sum(proportions)
535
+ if total_proposed > 1:
536
+ raise ValueError(
537
+ f"Invalid proportion: The sum of all proportions cannot be greater than 1. "
538
+ f"Proposed sum of proportions is {total_proposed}."
539
+ )
540
+ children_with_proportions = list(zip(proportions, children))
541
+ successes = {}
542
+ if netuid is not None:
543
+ success, message, ext_id = await set_children_extrinsic(
544
+ meshtensor=meshtensor,
545
+ wallet=wallet,
546
+ netuid=netuid,
547
+ hotkey=hotkey,
548
+ proxy=proxy,
549
+ children_with_proportions=children_with_proportions,
550
+ prompt=prompt,
551
+ wait_for_inclusion=wait_for_inclusion,
552
+ wait_for_finalization=wait_for_finalization,
553
+ )
554
+ successes[netuid] = {
555
+ "success": success,
556
+ "error": message,
557
+ "completion_block": None,
558
+ "set_block": None,
559
+ "extrinsic_identifier": ext_id,
560
+ }
561
+ # Result
562
+ if success:
563
+ if wait_for_inclusion and wait_for_finalization:
564
+ current_block, completion_block = await get_childkey_completion_block(
565
+ meshtensor, netuid
566
+ )
567
+ successes[netuid]["completion_block"] = completion_block
568
+ successes[netuid]["set_block"] = current_block
569
+ console.print(
570
+ f"Your childkey request has been submitted. It will be completed around block {completion_block}. "
571
+ f"The current block is {current_block}"
572
+ )
573
+ print_success("Set children hotkeys.")
574
+ else:
575
+ print_error(f"Unable to set children hotkeys. {message}")
576
+ else:
577
+ # set children on all subnets that parent is registered on
578
+ netuids = await meshtensor.get_all_subnet_netuids()
579
+ for netuid_ in netuids:
580
+ if netuid_ == 0: # dont include root network
581
+ continue
582
+ console.print(f"Setting children on netuid {netuid_}.")
583
+ success, message, ext_id = await set_children_extrinsic(
584
+ meshtensor=meshtensor,
585
+ wallet=wallet,
586
+ netuid=netuid_,
587
+ hotkey=hotkey,
588
+ proxy=proxy,
589
+ children_with_proportions=children_with_proportions,
590
+ prompt=prompt,
591
+ wait_for_inclusion=True,
592
+ wait_for_finalization=False,
593
+ )
594
+ current_block, completion_block = await get_childkey_completion_block(
595
+ meshtensor, netuid_
596
+ )
597
+ successes[netuid_] = {
598
+ "success": success,
599
+ "error": message,
600
+ "completion_block": completion_block,
601
+ "set_block": current_block,
602
+ "extrinsic_identifier": ext_id,
603
+ }
604
+ console.print(
605
+ f"Your childkey request for netuid {netuid_} has been submitted. It will be completed around "
606
+ f"block {completion_block}. The current block is {current_block}."
607
+ )
608
+ print_success("Sent set children request for all subnets.")
609
+ if json_output:
610
+ json_console.print(json.dumps(successes))
611
+
612
+
613
+ async def revoke_children(
614
+ wallet: Wallet,
615
+ meshtensor: "MeshtensorInterface",
616
+ netuid: Optional[int] = None,
617
+ proxy: Optional[str] = None,
618
+ wait_for_inclusion: bool = True,
619
+ wait_for_finalization: bool = True,
620
+ prompt: bool = True,
621
+ json_output: bool = False,
622
+ ):
623
+ """
624
+ Revokes the children hotkeys associated with a given network identifier (netuid).
625
+ """
626
+ dict_output = {}
627
+ if netuid is not None:
628
+ success, message, ext_id = await set_children_extrinsic(
629
+ meshtensor=meshtensor,
630
+ wallet=wallet,
631
+ netuid=netuid,
632
+ hotkey=get_hotkey_pub_ss58(wallet),
633
+ children_with_proportions=[],
634
+ proxy=proxy,
635
+ prompt=prompt,
636
+ wait_for_inclusion=wait_for_inclusion,
637
+ wait_for_finalization=wait_for_finalization,
638
+ )
639
+ dict_output[netuid] = {
640
+ "success": success,
641
+ "error": message,
642
+ "set_block": None,
643
+ "completion_block": None,
644
+ "extrinsic_identifier": ext_id,
645
+ }
646
+
647
+ # Result
648
+ if success:
649
+ current_block, completion_block = await get_childkey_completion_block(
650
+ meshtensor, netuid
651
+ )
652
+ dict_output[netuid]["completion_block"] = completion_block
653
+ dict_output[netuid]["set_block"] = current_block
654
+ console.print(
655
+ f":white_heavy_check_mark: Your childkey revocation request for netuid {netuid} has been submitted. "
656
+ f"It will be completed around block {completion_block}. The current block is {current_block}"
657
+ )
658
+ else:
659
+ console.print(f"Unable to revoke children hotkeys. {message}")
660
+ else:
661
+ # revoke children from ALL netuids
662
+ netuids = await meshtensor.get_all_subnet_netuids()
663
+ for netuid_ in netuids:
664
+ if netuid_ == 0: # dont include root network
665
+ continue
666
+ console.print(f"Revoking children from netuid {netuid_}.")
667
+ success, message, ext_id = await set_children_extrinsic(
668
+ meshtensor=meshtensor,
669
+ wallet=wallet,
670
+ netuid=netuid, # TODO should this be able to allow netuid = None ?
671
+ hotkey=get_hotkey_pub_ss58(wallet),
672
+ children_with_proportions=[],
673
+ proxy=proxy,
674
+ prompt=prompt,
675
+ wait_for_inclusion=True,
676
+ wait_for_finalization=False,
677
+ )
678
+ dict_output[netuid_] = {
679
+ "success": success,
680
+ "error": message,
681
+ "set_block": None,
682
+ "completion_block": None,
683
+ "extrinsic_identifier": ext_id,
684
+ }
685
+ if success:
686
+ current_block, completion_block = await get_childkey_completion_block(
687
+ meshtensor, netuid_
688
+ )
689
+ dict_output[netuid_]["completion_block"] = completion_block
690
+ dict_output[netuid_]["set_block"] = current_block
691
+ console.print(
692
+ f":white_heavy_check_mark: Your childkey revocation request for netuid {netuid_} has been "
693
+ f"submitted. It will be completed around block {completion_block}. The current block "
694
+ f"is {current_block}"
695
+ )
696
+ else:
697
+ print_error(
698
+ f"Childkey revocation failed for netuid {netuid_}: {message}."
699
+ )
700
+ if json_output:
701
+ json_console.print(json.dumps(dict_output))
702
+
703
+
704
+ async def childkey_take(
705
+ wallet: Wallet,
706
+ meshtensor: "MeshtensorInterface",
707
+ take: Optional[float],
708
+ hotkey: Optional[str] = None,
709
+ netuid: Optional[int] = None,
710
+ proxy: Optional[str] = None,
711
+ wait_for_inclusion: bool = True,
712
+ wait_for_finalization: bool = True,
713
+ prompt: bool = True,
714
+ decline: bool = False,
715
+ quiet: bool = False,
716
+ ) -> list[tuple[Optional[int], bool, Optional[str]]]:
717
+ """
718
+ Get or Set childkey take.
719
+
720
+ Returns:
721
+ List of (netuid, success, extrinsic identifier) for specified netuid (or all) and their success in setting take
722
+ """
723
+
724
+ def validate_take_value(take_value: float) -> bool:
725
+ if not (0 <= take_value <= 0.18):
726
+ print_error(f"Invalid take value: {take_value}")
727
+ return False
728
+ return True
729
+
730
+ async def display_chk_take(ss58, take_netuid) -> float:
731
+ """Print single key take for hotkey and netuid"""
732
+ chk_take = await get_childkey_take(
733
+ meshtensor=meshtensor, netuid=take_netuid, hotkey=ss58
734
+ )
735
+ if chk_take is None:
736
+ chk_take = 0
737
+ chk_take = u16_to_float(chk_take)
738
+ console.print(
739
+ f"Child take for {ss58} is: {chk_take * 100:.2f}% on netuid {take_netuid}."
740
+ )
741
+ return chk_take
742
+
743
+ async def chk_all_subnets(ss58):
744
+ """Aggregate data for childkey take from all subnets"""
745
+ all_netuids = await meshtensor.get_all_subnet_netuids()
746
+ takes = []
747
+ for subnet in all_netuids:
748
+ if subnet == 0:
749
+ continue
750
+ curr_take = await get_childkey_take(
751
+ meshtensor=meshtensor, netuid=subnet, hotkey=ss58
752
+ )
753
+ if curr_take is not None:
754
+ take_value = u16_to_float(curr_take)
755
+ takes.append((subnet, take_value * 100))
756
+ table = Table(
757
+ title=f"Current Child Takes for [bright_magenta]{ss58}[/bright_magenta]"
758
+ )
759
+ table.add_column("Netuid", justify="center", style="cyan")
760
+ table.add_column("Take (%)", justify="right", style="magenta")
761
+
762
+ for take_netuid, take_value in takes:
763
+ table.add_row(str(take_netuid), f"{take_value:.2f}%")
764
+
765
+ console.print(table)
766
+
767
+ async def set_chk_take_subnet(
768
+ subnet: int, chk_take: float
769
+ ) -> tuple[bool, Optional[str]]:
770
+ """Set the childkey take for a single subnet"""
771
+ success_, message, ext_id_ = await set_childkey_take_extrinsic(
772
+ meshtensor=meshtensor,
773
+ wallet=wallet,
774
+ netuid=subnet,
775
+ hotkey=get_hotkey_pub_ss58(wallet),
776
+ take=chk_take,
777
+ proxy=proxy,
778
+ prompt=prompt,
779
+ wait_for_inclusion=wait_for_inclusion,
780
+ wait_for_finalization=wait_for_finalization,
781
+ )
782
+ # Result
783
+ if success_:
784
+ print_success("Set childkey take.")
785
+ console.print(
786
+ f"The childkey take for {get_hotkey_pub_ss58(wallet)} is now set to {take * 100:.2f}%."
787
+ )
788
+ return True, ext_id_
789
+ else:
790
+ print_error(f"Unable to set childkey take. {message}")
791
+ return False, ext_id_
792
+
793
+ # Print childkey take for other user and return (dont offer to change take rate)
794
+ wallet_hk = get_hotkey_pub_ss58(wallet)
795
+ if not hotkey or hotkey == wallet_hk:
796
+ hotkey = wallet_hk
797
+ if hotkey != wallet_hk or not take:
798
+ # display childkey take for other users
799
+ if netuid:
800
+ await display_chk_take(hotkey, netuid)
801
+ if take:
802
+ console.print(
803
+ f"Hotkey {hotkey} not associated with wallet {wallet.name}."
804
+ )
805
+ return [(netuid, False, None)]
806
+ else:
807
+ # show child hotkey take on all subnets
808
+ await chk_all_subnets(hotkey)
809
+ if take:
810
+ console.print(
811
+ f"Hotkey {hotkey} not associated with wallet {wallet.name}."
812
+ )
813
+ return [(netuid, False, None)]
814
+
815
+ # Validate child SS58 addresses
816
+ if not take:
817
+ if not confirm_action(
818
+ "Would you like to change the child take?", decline=decline, quiet=quiet
819
+ ):
820
+ return [(netuid, False, None)]
821
+ new_take_value = -1.0
822
+ while not validate_take_value(new_take_value):
823
+ new_take_value = FloatPrompt.ask(
824
+ "Enter the new take value (between 0 and 0.18)"
825
+ )
826
+ take = new_take_value
827
+ else:
828
+ if not validate_take_value(take):
829
+ return [(netuid, False, None)]
830
+
831
+ if netuid:
832
+ success, ext_id = await set_chk_take_subnet(subnet=netuid, chk_take=take)
833
+ return [(netuid, success, ext_id)]
834
+ else:
835
+ new_take_netuids = IntPrompt.ask(
836
+ "Enter netuid (leave blank for all)", default=None, show_default=True
837
+ )
838
+
839
+ if new_take_netuids:
840
+ success, ext_id = await set_chk_take_subnet(
841
+ subnet=new_take_netuids, chk_take=take
842
+ )
843
+ return [(new_take_netuids, success, ext_id)]
844
+
845
+ else:
846
+ netuids = await meshtensor.get_all_subnet_netuids()
847
+ output_list = []
848
+ for netuid_ in netuids:
849
+ if netuid_ == 0:
850
+ continue
851
+ console.print(f"Setting take of {take * 100:.2f}% on netuid {netuid_}.")
852
+ result, _, ext_id = await set_childkey_take_extrinsic(
853
+ meshtensor=meshtensor,
854
+ wallet=wallet,
855
+ netuid=netuid_,
856
+ hotkey=wallet_hk,
857
+ take=take,
858
+ proxy=proxy,
859
+ prompt=prompt,
860
+ wait_for_inclusion=True,
861
+ wait_for_finalization=False,
862
+ )
863
+ output_list.append((netuid_, result, ext_id))
864
+ print_success(f"Sent childkey take of {take * 100:.2f}% to all subnets.")
865
+ return output_list