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,550 @@
1
+ # The MIT License (MIT)
2
+ # Copyright © 2021 Yuma Rao
3
+ # Copyright © 2023 Opentensor Foundation
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the “Software”), to deal in the Software without restriction, including without limitation
6
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7
+ # and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of
10
+ # the Software.
11
+
12
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
13
+ # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
16
+ # DEALINGS IN THE SOFTWARE.
17
+
18
+ import asyncio
19
+ import hashlib
20
+ from typing import Union, List, TYPE_CHECKING, Optional
21
+
22
+ from meshtensor_wallet import Wallet, Keypair
23
+ import numpy as np
24
+ from numpy.typing import NDArray
25
+ from rich.table import Table, Column
26
+ from scalecodec import ScaleBytes, U16, Vec
27
+ from async_substrate_interface.errors import SubstrateRequestException
28
+
29
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
30
+ from meshtensor_cli.src.meshtensor.extrinsics.registration import is_hotkey_registered
31
+ from meshtensor_cli.src.meshtensor.utils import (
32
+ confirm_action,
33
+ console,
34
+ print_error,
35
+ print_success,
36
+ u16_normalized_float,
37
+ print_verbose,
38
+ format_error_message,
39
+ unlock_key,
40
+ get_hotkey_pub_ss58,
41
+ print_extrinsic_id,
42
+ )
43
+
44
+ if TYPE_CHECKING:
45
+ from meshtensor_cli.src.meshtensor.minigraph import MiniGraph
46
+
47
+ U32_MAX = 4294967295
48
+ U16_MAX = 65535
49
+
50
+
51
+ async def get_current_weights_for_uid(
52
+ meshtensor: MeshtensorInterface,
53
+ netuid: int,
54
+ uid: int,
55
+ ) -> dict[int, float]:
56
+ """
57
+ Fetches the current weights set by a specific UID on a subnet.
58
+
59
+ Args:
60
+ meshtensor: The MeshtensorInterface instance.
61
+ netuid: The network UID (0 for root network).
62
+ uid: The UID of the neuron whose weights to fetch.
63
+
64
+ Returns:
65
+ A dictionary mapping destination netuid to normalized weight (0.0-1.0).
66
+ """
67
+ weights_data = await meshtensor.weights(netuid=netuid)
68
+ current_weights: dict[int, float] = {}
69
+
70
+ for validator_uid, weight_list in weights_data:
71
+ if validator_uid == uid:
72
+ for dest_netuid, raw_weight in weight_list:
73
+ current_weights[dest_netuid] = u16_normalized_float(raw_weight)
74
+ break
75
+
76
+ return current_weights
77
+
78
+
79
+ async def get_limits(meshtensor: MeshtensorInterface) -> tuple[int, float]:
80
+ # Get weight restrictions.
81
+ maw, mwl = await asyncio.gather(
82
+ meshtensor.get_hyperparameter("MinAllowedWeights", netuid=0),
83
+ meshtensor.get_hyperparameter("MaxWeightsLimit", netuid=0),
84
+ )
85
+ min_allowed_weights = int(maw)
86
+ max_weight_limit = u16_normalized_float(int(mwl))
87
+ return min_allowed_weights, max_weight_limit
88
+
89
+
90
+ def normalize_max_weight(
91
+ x: NDArray[np.float32], limit: float = 0.1
92
+ ) -> NDArray[np.float32]:
93
+ """
94
+ Normalizes the tensor x so that sum(x) = 1 and the max value is not greater than the limit.
95
+
96
+ :param x: Tensor to be max_value normalized.
97
+ :param limit: Max value after normalization.
98
+
99
+ :return: Normalized x tensor.
100
+ """
101
+ epsilon = 1e-7 # For numerical stability after normalization
102
+
103
+ weights = x.copy()
104
+ values = np.sort(weights)
105
+
106
+ if x.sum() == 0 or x.shape[0] * limit <= 1:
107
+ return np.ones_like(x) / x.shape[0]
108
+ else:
109
+ estimation = values / values.sum()
110
+
111
+ if estimation.max() <= limit:
112
+ return weights / weights.sum()
113
+
114
+ # Find the cumulative sum and sorted tensor
115
+ cumsum = np.cumsum(estimation, 0)
116
+
117
+ # Determine the index of cutoff
118
+ estimation_sum = np.array(
119
+ [(len(values) - i - 1) * estimation[i] for i in range(len(values))]
120
+ )
121
+ n_values = (estimation / (estimation_sum + cumsum + epsilon) < limit).sum()
122
+
123
+ # Determine the cutoff based on the index
124
+ cutoff_scale = (limit * cumsum[n_values - 1] - epsilon) / (
125
+ 1 - (limit * (len(estimation) - n_values))
126
+ )
127
+ cutoff = cutoff_scale * values.sum()
128
+
129
+ # Applying the cutoff
130
+ weights[weights > cutoff] = cutoff
131
+
132
+ y = weights / weights.sum()
133
+
134
+ return y
135
+
136
+
137
+ def convert_weights_and_uids_for_emit(
138
+ uids: NDArray[np.int64],
139
+ weights: NDArray[np.float32],
140
+ ) -> tuple[List[int], List[int]]:
141
+ """Converts weights into integer u32 representation that sum to MAX_INT_WEIGHT.
142
+
143
+ :param uids: Tensor of uids as destinations for passed weights.
144
+ :param weights: Tensor of weights.
145
+
146
+ :return: (weight_uids, weight_vals)
147
+ """
148
+ # Checks.
149
+ weights = weights.tolist()
150
+ uids = uids.tolist()
151
+ if min(weights) < 0:
152
+ raise ValueError(
153
+ "Passed weight is negative cannot exist on chain {}".format(weights)
154
+ )
155
+ if min(uids) < 0:
156
+ raise ValueError("Passed uid is negative cannot exist on chain {}".format(uids))
157
+ if len(uids) != len(weights):
158
+ raise ValueError(
159
+ "Passed weights and uids must have the same length, got {} and {}".format(
160
+ len(uids), len(weights)
161
+ )
162
+ )
163
+ if sum(weights) == 0:
164
+ return [], [] # Nothing to set on chain.
165
+ else:
166
+ max_weight = float(max(weights))
167
+ weights = [
168
+ float(value) / max_weight for value in weights
169
+ ] # max-upscale values (max_weight = 1).
170
+
171
+ weight_vals = []
172
+ weight_uids = []
173
+ for i, (weight_i, uid_i) in enumerate(list(zip(weights, uids))):
174
+ uint16_val = round(
175
+ float(weight_i) * int(U16_MAX)
176
+ ) # convert to int representation.
177
+
178
+ # Filter zeros
179
+ if uint16_val != 0: # Filter zeros
180
+ weight_vals.append(uint16_val)
181
+ weight_uids.append(uid_i)
182
+
183
+ return weight_uids, weight_vals
184
+
185
+
186
+ async def process_weights_for_netuid(
187
+ uids: NDArray[np.int64],
188
+ weights: NDArray[np.float32],
189
+ netuid: int,
190
+ meshtensor: MeshtensorInterface,
191
+ metagraph: "MiniGraph" = None,
192
+ exclude_quantile: int = 0,
193
+ ) -> tuple[NDArray[np.int64], NDArray[np.float32]]:
194
+ # meshtensor.logging.debug("process_weights_for_netuid()")
195
+ # meshtensor.logging.debug("weights", weights)
196
+ # meshtensor.logging.debug("netuid", netuid)
197
+ # meshtensor.logging.debug("meshtensor", meshtensor)
198
+ # meshtensor.logging.debug("metagraph", metagraph)
199
+
200
+ # Get latest metagraph from chain if metagraph is None.
201
+ if metagraph is None:
202
+ metagraph = meshtensor.metagraph(netuid)
203
+
204
+ if not isinstance(weights, np.float32):
205
+ weights = weights.astype(np.float32)
206
+
207
+ # Network configuration parameters from a meshtensor.
208
+ # These parameters determine the range of acceptable weights for each neuron.
209
+ quantile = exclude_quantile / U16_MAX
210
+ min_allowed_weights, max_weight_limit = await get_limits(meshtensor)
211
+ # meshtensor.logging.debug("quantile", quantile)
212
+ # meshtensor.logging.debug("min_allowed_weights", min_allowed_weights)
213
+ # meshtensor.logging.debug("max_weight_limit", max_weight_limit)
214
+
215
+ # Find all non zero weights.
216
+ non_zero_weight_idx = np.argwhere(weights > 0).squeeze(axis=1)
217
+ non_zero_weight_uids = uids[non_zero_weight_idx]
218
+ non_zero_weights = weights[non_zero_weight_idx]
219
+ nzw_size = non_zero_weights.size
220
+ if nzw_size == 0 or metagraph.n < min_allowed_weights:
221
+ # meshtensor.logging.warning("No non-zero weights returning all ones.")
222
+ final_weights = np.ones(metagraph.n, dtype=np.int64) / metagraph.n
223
+ # meshtensor.logging.debug("final_weights", final_weights)
224
+ final_weights_count = np.arange(len(final_weights))
225
+ return final_weights_count, final_weights
226
+
227
+ elif nzw_size < min_allowed_weights:
228
+ # meshtensor.logging.warning(
229
+ # "No non-zero weights less than min allowed weight, returning all ones."
230
+ # )
231
+ weights = (
232
+ np.ones(metagraph.n, dtype=np.int64) * 1e-5
233
+ ) # creating minimum even non-zero weights
234
+ weights[non_zero_weight_idx] += non_zero_weights
235
+ # meshtensor.logging.debug("final_weights", weights)
236
+ normalized_weights = normalize_max_weight(x=weights, limit=max_weight_limit)
237
+ nw_arange = np.arange(len(normalized_weights))
238
+ return nw_arange, normalized_weights
239
+
240
+ # meshtensor.logging.debug("non_zero_weights", non_zero_weights)
241
+
242
+ # Compute the exclude quantile and find the weights in the lowest quantile
243
+ max_exclude = max(0, len(non_zero_weights) - min_allowed_weights) / len(
244
+ non_zero_weights
245
+ )
246
+ exclude_quantile = min([quantile, max_exclude])
247
+ lowest_quantile = np.quantile(non_zero_weights, exclude_quantile)
248
+ # meshtensor.logging.debug("max_exclude", max_exclude)
249
+ # meshtensor.logging.debug("exclude_quantile", exclude_quantile)
250
+ # meshtensor.logging.debug("lowest_quantile", lowest_quantile)
251
+
252
+ # Exclude all weights below the allowed quantile.
253
+ non_zero_weight_uids = non_zero_weight_uids[lowest_quantile <= non_zero_weights]
254
+ non_zero_weights = non_zero_weights[lowest_quantile <= non_zero_weights]
255
+ # meshtensor.logging.debug("non_zero_weight_uids", non_zero_weight_uids)
256
+ # meshtensor.logging.debug("non_zero_weights", non_zero_weights)
257
+
258
+ # Normalize weights and return.
259
+ normalized_weights = normalize_max_weight(
260
+ x=non_zero_weights, limit=max_weight_limit
261
+ )
262
+ # meshtensor.logging.debug("final_weights", normalized_weights)
263
+
264
+ return non_zero_weight_uids, normalized_weights
265
+
266
+
267
+ def generate_weight_hash(
268
+ address: str,
269
+ netuid: int,
270
+ uids: List[int],
271
+ values: List[int],
272
+ version_key: int,
273
+ salt: List[int],
274
+ ) -> str:
275
+ """
276
+ Generate a valid commit hash from the provided weights.
277
+
278
+ :param address: The account identifier. Wallet ss58_address.
279
+ :param netuid: The network unique identifier.
280
+ :param uids: The list of UIDs.
281
+ :param salt: The salt to add to hash.
282
+ :param values: The list of weight values.
283
+ :param version_key: The version key.
284
+
285
+ :return The generated commit hash.
286
+ """
287
+ # Encode data using SCALE codec
288
+ wallet_address = ScaleBytes(Keypair(ss58_address=address).public_key)
289
+ netuid = ScaleBytes(netuid.to_bytes(2, "little"))
290
+
291
+ vec_uids = Vec(data=None, sub_type="U16")
292
+ vec_uids.value = [U16(ScaleBytes(uid.to_bytes(2, "little"))) for uid in uids]
293
+ uids = ScaleBytes(vec_uids.encode().data)
294
+
295
+ vec_values = Vec(data=None, sub_type="U16")
296
+ vec_values.value = [
297
+ U16(ScaleBytes(value.to_bytes(2, "little"))) for value in values
298
+ ]
299
+ values = ScaleBytes(vec_values.encode().data)
300
+
301
+ version_key = ScaleBytes(version_key.to_bytes(8, "little"))
302
+
303
+ vec_salt = Vec(data=None, sub_type="U16")
304
+ vec_salt.value = [U16(ScaleBytes(salts.to_bytes(2, "little"))) for salts in salt]
305
+ salt = ScaleBytes(vec_salt.encode().data)
306
+
307
+ data = wallet_address + netuid + uids + values + salt + version_key
308
+
309
+ # Generate Blake2b hash of the data tuple
310
+ blake2b_hash = hashlib.blake2b(data.data, digest_size=32)
311
+
312
+ # Convert the hash to hex string and add "0x" prefix
313
+ commit_hash = "0x" + blake2b_hash.hexdigest()
314
+
315
+ return commit_hash
316
+
317
+
318
+ async def root_register_extrinsic(
319
+ meshtensor: MeshtensorInterface,
320
+ wallet: Wallet,
321
+ wait_for_inclusion: bool = True,
322
+ wait_for_finalization: bool = True,
323
+ proxy: Optional[str] = None,
324
+ ) -> tuple[bool, str, Optional[str]]:
325
+ r"""Registers the wallet to root network.
326
+
327
+ :param meshtensor: The MeshtensorInterface object
328
+ :param wallet: Meshtensor wallet object.
329
+ :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
330
+ `False` if the extrinsic fails to enter the block within the timeout.
331
+ :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
332
+ or returns `False` if the extrinsic fails to be finalized within the timeout.
333
+ :param proxy: Optional proxy to use for making the call.
334
+
335
+ :return: (success, msg), with success being `True` if extrinsic was finalized or included in the block. If we did
336
+ not wait for finalization/inclusion, the response is `True`.
337
+ """
338
+
339
+ if not (unlock := unlock_key(wallet)).success:
340
+ return False, unlock.message, None
341
+
342
+ print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
343
+ is_registered = await is_hotkey_registered(
344
+ meshtensor, netuid=0, hotkey_ss58=get_hotkey_pub_ss58(wallet)
345
+ )
346
+ if is_registered:
347
+ print_success("Already registered on root network.")
348
+ return True, "Already registered on root network", None
349
+
350
+ with console.status(":satellite: Registering to root network...", spinner="earth"):
351
+ call = await meshtensor.substrate.compose_call(
352
+ call_module="MeshtensorModule",
353
+ call_function="root_register",
354
+ call_params={"hotkey": get_hotkey_pub_ss58(wallet)},
355
+ )
356
+ success, err_msg, ext_receipt = await meshtensor.sign_and_send_extrinsic(
357
+ call,
358
+ wallet=wallet,
359
+ wait_for_inclusion=wait_for_inclusion,
360
+ wait_for_finalization=wait_for_finalization,
361
+ proxy=proxy,
362
+ )
363
+
364
+ if not success:
365
+ print_error(f"Failed: {err_msg}")
366
+ await asyncio.sleep(0.5)
367
+ return False, err_msg, None
368
+
369
+ # Successful registration, final check for neuron and pubkey
370
+ else:
371
+ ext_id = await ext_receipt.get_extrinsic_identifier()
372
+ await print_extrinsic_id(ext_receipt)
373
+ uid = await meshtensor.query(
374
+ module="MeshtensorModule",
375
+ storage_function="Uids",
376
+ params=[0, get_hotkey_pub_ss58(wallet)],
377
+ )
378
+ if uid is not None:
379
+ print_success(f"Registered with UID {uid}")
380
+ return True, f"Registered with UID {uid}", ext_id
381
+ else:
382
+ # neuron not found, try again
383
+ print_error("Unknown error. Neuron not found.")
384
+ return False, "Unknown error. Neuron not found.", ext_id
385
+
386
+
387
+ async def set_root_weights_extrinsic(
388
+ meshtensor: MeshtensorInterface,
389
+ wallet: Wallet,
390
+ netuids: Union[NDArray[np.int64], list[int]],
391
+ weights: Union[NDArray[np.float32], list[float]],
392
+ version_key: int = 0,
393
+ wait_for_inclusion: bool = False,
394
+ wait_for_finalization: bool = False,
395
+ prompt: bool = False,
396
+ decline: bool = False,
397
+ quiet: bool = False,
398
+ ) -> bool:
399
+ """Sets the given weights and values on chain for wallet hotkey account.
400
+
401
+ :param meshtensor: The MeshtensorInterface object
402
+ :param wallet: Meshtensor wallet object.
403
+ :param netuids: The `netuid` of the subnet to set weights for.
404
+ :param weights: Weights to set. These must be `float` s and must correspond to the passed `netuid` s.
405
+ :param version_key: The version key of the validator.
406
+ :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
407
+ `False` if the extrinsic fails to enter the block within the timeout.
408
+ :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
409
+ or returns `False` if the extrinsic fails to be finalized within the timeout.
410
+ :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
411
+ :return: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion,
412
+ the response is `True`.
413
+ """
414
+
415
+ async def _do_set_weights():
416
+ call = await meshtensor.substrate.compose_call(
417
+ call_module="MeshtensorModule",
418
+ call_function="set_root_weights",
419
+ call_params={
420
+ "dests": weight_uids,
421
+ "weights": weight_vals,
422
+ "netuid": 0,
423
+ "version_key": version_key,
424
+ "hotkey": get_hotkey_pub_ss58(wallet),
425
+ },
426
+ )
427
+ # Period dictates how long the extrinsic will stay as part of waiting pool
428
+ extrinsic = await meshtensor.substrate.create_signed_extrinsic(
429
+ call=call,
430
+ keypair=wallet.coldkey,
431
+ era={"period": 5},
432
+ )
433
+ response = await meshtensor.substrate.submit_extrinsic(
434
+ extrinsic,
435
+ wait_for_inclusion=wait_for_inclusion,
436
+ wait_for_finalization=wait_for_finalization,
437
+ )
438
+ # We only wait here if we expect finalization.
439
+ if not wait_for_finalization and not wait_for_inclusion:
440
+ return True, "Not waiting for finalization or inclusion."
441
+
442
+ if await response.is_success:
443
+ return True, "Successfully set weights."
444
+ else:
445
+ return False, await response.error_message
446
+
447
+ my_uid = await meshtensor.query(
448
+ "MeshtensorModule", "Uids", [0, get_hotkey_pub_ss58(wallet)]
449
+ )
450
+
451
+ if my_uid is None:
452
+ print_error("Your hotkey is not registered to the root network")
453
+ return False
454
+
455
+ if not unlock_key(wallet).success:
456
+ return False
457
+
458
+ # First convert types.
459
+ if isinstance(netuids, list):
460
+ netuids = np.array(netuids, dtype=np.int64)
461
+ if isinstance(weights, list):
462
+ weights = np.array(weights, dtype=np.float32)
463
+
464
+ print_verbose("Fetching weight limits")
465
+ min_allowed_weights, max_weight_limit = await get_limits(meshtensor)
466
+
467
+ # Get non zero values.
468
+ non_zero_weight_idx = np.argwhere(weights > 0).squeeze(axis=1)
469
+ non_zero_weights = weights[non_zero_weight_idx]
470
+ if non_zero_weights.size < min_allowed_weights:
471
+ raise ValueError(
472
+ "The minimum number of weights required to set weights is {}, got {}".format(
473
+ min_allowed_weights, non_zero_weights.size
474
+ )
475
+ )
476
+
477
+ # Normalize the weights to max value.
478
+ print_verbose("Normalizing weights")
479
+ formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit)
480
+ console.print(
481
+ f"\nRaw weights -> Normalized weights: \n\t{weights} -> \n\t{formatted_weights}\n"
482
+ )
483
+
484
+ # Ask before moving on.
485
+ if prompt:
486
+ # Fetch current weights for comparison
487
+ print_verbose("Fetching current weights for comparison")
488
+ current_weights = await get_current_weights_for_uid(
489
+ meshtensor, netuid=0, uid=my_uid
490
+ )
491
+
492
+ table = Table(
493
+ Column("[dark_orange]Netuid", justify="center", style="bold green"),
494
+ Column("[dark_orange]Current", justify="center", style="dim"),
495
+ Column("[dark_orange]New", justify="center", style="bold light_goldenrod2"),
496
+ Column("[dark_orange]Change", justify="center"),
497
+ expand=False,
498
+ show_edge=False,
499
+ )
500
+
501
+ for netuid, new_weight in zip(netuids, formatted_weights):
502
+ current_weight = current_weights.get(netuid, 0.0)
503
+ diff = new_weight - current_weight
504
+
505
+ # Format the difference with color and sign
506
+ if diff > 0.00000001:
507
+ diff_str = f"[green]+{diff:.8f}[/green]"
508
+ elif diff < -0.00000001:
509
+ diff_str = f"[red]{diff:.8f}[/red]"
510
+ else:
511
+ diff_str = "[dim]0.00000000[/dim]"
512
+
513
+ table.add_row(
514
+ str(netuid),
515
+ f"{current_weight:.8f}",
516
+ f"{new_weight:.8f}",
517
+ diff_str,
518
+ )
519
+
520
+ console.print(table)
521
+ if not confirm_action(
522
+ "\nDo you want to set these root weights?",
523
+ decline=decline,
524
+ quiet=quiet,
525
+ ):
526
+ return False
527
+
528
+ try:
529
+ with console.status("Setting root weights...", spinner="earth"):
530
+ weight_uids, weight_vals = convert_weights_and_uids_for_emit(
531
+ netuids, weights
532
+ )
533
+
534
+ success, error_message = await _do_set_weights()
535
+
536
+ if not wait_for_finalization and not wait_for_inclusion:
537
+ return True
538
+
539
+ if success is True:
540
+ print_success("Finalized")
541
+ return True
542
+ else:
543
+ fmt_err = format_error_message(error_message)
544
+ print_error(f"Failed: {fmt_err}")
545
+ return False
546
+
547
+ except SubstrateRequestException as e:
548
+ fmt_err = format_error_message(e)
549
+ print_error("Failed: error:{}".format(fmt_err))
550
+ return False