bittensor-cli 8.2.0rc12__py3-none-any.whl → 8.2.0rc14__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.
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  from functools import partial
3
3
 
4
- from typing import TYPE_CHECKING, Optional, Sequence, Union, cast
4
+ from typing import TYPE_CHECKING, Optional
5
5
  import typer
6
6
 
7
7
  from bittensor_wallet import Wallet
@@ -25,7 +25,6 @@ from bittensor_cli.src.bittensor.utils import (
25
25
  print_error,
26
26
  get_hotkey_wallets_for_wallet,
27
27
  is_valid_ss58_address,
28
- u16_normalized_float,
29
28
  format_error_message,
30
29
  group_subnets,
31
30
  millify_tao,
@@ -36,797 +35,6 @@ if TYPE_CHECKING:
36
35
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
37
36
 
38
37
 
39
- # Helpers and Extrinsics
40
-
41
-
42
- async def _get_threshold_amount(
43
- subtensor: "SubtensorInterface", block_hash: str
44
- ) -> Balance:
45
- mrs = await subtensor.substrate.query(
46
- module="SubtensorModule",
47
- storage_function="NominatorMinRequiredStake",
48
- block_hash=block_hash,
49
- )
50
- min_req_stake: Balance = Balance.from_rao(mrs)
51
- return min_req_stake
52
-
53
-
54
- async def _check_threshold_amount(
55
- subtensor: "SubtensorInterface",
56
- sb: Balance,
57
- block_hash: str,
58
- min_req_stake: Optional[Balance] = None,
59
- ) -> tuple[bool, Balance]:
60
- """
61
- Checks if the new stake balance will be above the minimum required stake threshold.
62
-
63
- :param sb: the balance to check for threshold limits.
64
-
65
- :return: (success, threshold)
66
- `True` if the staking balance is above the threshold, or `False` if the staking balance is below the
67
- threshold.
68
- The threshold balance required to stake.
69
- """
70
- if not min_req_stake:
71
- min_req_stake = await _get_threshold_amount(subtensor, block_hash)
72
-
73
- if min_req_stake > sb:
74
- return False, min_req_stake
75
- else:
76
- return True, min_req_stake
77
-
78
-
79
- async def add_stake_extrinsic(
80
- subtensor: "SubtensorInterface",
81
- wallet: Wallet,
82
- old_balance: Balance,
83
- hotkey_ss58: Optional[str] = None,
84
- amount: Optional[Balance] = None,
85
- wait_for_inclusion: bool = True,
86
- wait_for_finalization: bool = False,
87
- prompt: bool = False,
88
- ) -> bool:
89
- """
90
- Adds the specified amount of stake to passed hotkey `uid`.
91
-
92
- :param subtensor: the initialized SubtensorInterface object to use
93
- :param wallet: Bittensor wallet object.
94
- :param old_balance: the balance prior to the staking
95
- :param hotkey_ss58: The `ss58` address of the hotkey account to stake to defaults to the wallet's hotkey.
96
- :param amount: Amount to stake as Bittensor balance, `None` if staking all.
97
- :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
98
- `False` if the extrinsic fails to enter the block within the timeout.
99
- :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
100
- or returns `False` if the extrinsic fails to be finalized within the timeout.
101
- :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
102
-
103
- :return: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for
104
- finalization/inclusion, the response is `True`.
105
- """
106
-
107
- # Decrypt keys,
108
- try:
109
- wallet.unlock_coldkey()
110
- except KeyFileError:
111
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
112
- return False
113
-
114
- # Default to wallet's own hotkey if the value is not passed.
115
- if hotkey_ss58 is None:
116
- hotkey_ss58 = wallet.hotkey.ss58_address
117
-
118
- # Flag to indicate if we are using the wallet's own hotkey.
119
- own_hotkey: bool
120
-
121
- with console.status(
122
- f":satellite: Syncing with chain: [white]{subtensor}[/white] ...",
123
- spinner="aesthetic",
124
- ) as status:
125
- block_hash = await subtensor.substrate.get_chain_head()
126
- # Get hotkey owner
127
- print_verbose("Confirming hotkey owner", status)
128
- hotkey_owner = await subtensor.get_hotkey_owner(
129
- hotkey_ss58=hotkey_ss58, block_hash=block_hash
130
- )
131
- own_hotkey = wallet.coldkeypub.ss58_address == hotkey_owner
132
- if not own_hotkey:
133
- # This is not the wallet's own hotkey, so we are delegating.
134
- if not await subtensor.is_hotkey_delegate(
135
- hotkey_ss58, block_hash=block_hash
136
- ):
137
- err_console.print(
138
- f"Hotkey {hotkey_ss58} is not a delegate on the chain."
139
- )
140
- return False
141
-
142
- # Get hotkey take
143
- hk_result = await subtensor.substrate.query(
144
- module="SubtensorModule",
145
- storage_function="Delegates",
146
- params=[hotkey_ss58],
147
- block_hash=block_hash,
148
- )
149
- hotkey_take = u16_normalized_float(hk_result or 0)
150
- else:
151
- hotkey_take = None
152
-
153
- # Get current stake
154
- print_verbose("Fetching current stake", status)
155
- old_stake = await subtensor.get_stake_for_coldkey_and_hotkey(
156
- coldkey_ss58=wallet.coldkeypub.ss58_address,
157
- hotkey_ss58=hotkey_ss58,
158
- block_hash=block_hash,
159
- )
160
-
161
- print_verbose("Fetching existential deposit", status)
162
- # Grab the existential deposit.
163
- existential_deposit = await subtensor.get_existential_deposit()
164
-
165
- # Convert to bittensor.Balance
166
- if amount is None:
167
- # Stake it all.
168
- staking_balance = Balance.from_tao(old_balance.tao)
169
- else:
170
- staking_balance = Balance.from_tao(amount)
171
-
172
- # Leave existential balance to keep key alive.
173
- if staking_balance > old_balance - existential_deposit:
174
- # If we are staking all, we need to leave at least the existential deposit.
175
- staking_balance = old_balance - existential_deposit
176
- else:
177
- staking_balance = staking_balance
178
-
179
- # Check enough to stake.
180
- if staking_balance > old_balance:
181
- err_console.print(
182
- f":cross_mark: [red]Not enough stake[/red]:[bold white]\n"
183
- f"\tbalance:\t{old_balance}\n"
184
- f"\tamount:\t{staking_balance}\n"
185
- f"\tcoldkey:\t{wallet.name}[/bold white]"
186
- )
187
- return False
188
-
189
- # If nominating, we need to check if the new stake balance will be above the minimum required stake threshold.
190
- if not own_hotkey:
191
- new_stake_balance = old_stake + staking_balance
192
- print_verbose("Fetching threshold amount")
193
- is_above_threshold, threshold = await _check_threshold_amount(
194
- subtensor, new_stake_balance, block_hash
195
- )
196
- if not is_above_threshold:
197
- err_console.print(
198
- f":cross_mark: [red]New stake balance of {new_stake_balance} is below the minimum required nomination"
199
- f" stake threshold {threshold}.[/red]"
200
- )
201
- return False
202
-
203
- # Ask before moving on.
204
- if prompt:
205
- if not own_hotkey:
206
- # We are delegating.
207
- if not Confirm.ask(
208
- f"Do you want to delegate:[bold white]\n"
209
- f"\tamount: {staking_balance}\n"
210
- f"\tto: {hotkey_ss58}\n"
211
- f"\ttake: {hotkey_take}\n[/bold white]"
212
- f"\towner: {hotkey_owner}\n"
213
- ):
214
- return False
215
- else:
216
- if not Confirm.ask(
217
- f"Do you want to stake:[bold white]\n"
218
- f"\tamount: {staking_balance}\n"
219
- f"\tto: {wallet.hotkey_str}\n"
220
- f"\taddress: {hotkey_ss58}[/bold white]\n"
221
- ):
222
- return False
223
-
224
- with console.status(
225
- f":satellite: Staking to: [bold white]{subtensor}[/bold white] ...",
226
- spinner="earth",
227
- ):
228
- call = await subtensor.substrate.compose_call(
229
- call_module="SubtensorModule",
230
- call_function="add_stake",
231
- call_params={"hotkey": hotkey_ss58, "amount_staked": staking_balance.rao},
232
- )
233
- staking_response, err_msg = await subtensor.sign_and_send_extrinsic(
234
- call, wallet, wait_for_inclusion, wait_for_finalization
235
- )
236
- if staking_response is True: # If we successfully staked.
237
- # We only wait here if we expect finalization.
238
- if not wait_for_finalization and not wait_for_inclusion:
239
- return True
240
-
241
- console.print(":white_heavy_check_mark: [green]Finalized[/green]")
242
- with console.status(
243
- f":satellite: Checking Balance on: [white]{subtensor}[/white] ..."
244
- ):
245
- new_block_hash = await subtensor.substrate.get_chain_head()
246
- new_balance, new_stake = await asyncio.gather(
247
- subtensor.get_balance(
248
- wallet.coldkeypub.ss58_address, block_hash=new_block_hash
249
- ),
250
- subtensor.get_stake_for_coldkey_and_hotkey(
251
- coldkey_ss58=wallet.coldkeypub.ss58_address,
252
- hotkey_ss58=hotkey_ss58,
253
- block_hash=new_block_hash,
254
- ),
255
- )
256
-
257
- console.print(
258
- f"Balance:\n"
259
- f"\t[blue]{old_balance}[/blue] :arrow_right: "
260
- f"[green]{new_balance[wallet.coldkeypub.ss58_address]}[/green]"
261
- )
262
- console.print(
263
- f"Stake:\n"
264
- f"\t[blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]"
265
- )
266
- return True
267
- else:
268
- err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
269
- return False
270
-
271
-
272
- async def add_stake_multiple_extrinsic(
273
- subtensor: "SubtensorInterface",
274
- wallet: Wallet,
275
- old_balance: Balance,
276
- hotkey_ss58s: list[str],
277
- amounts: Optional[list[Balance]] = None,
278
- wait_for_inclusion: bool = True,
279
- wait_for_finalization: bool = False,
280
- prompt: bool = False,
281
- ) -> bool:
282
- """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey.
283
-
284
- :param subtensor: The initialized SubtensorInterface object.
285
- :param wallet: Bittensor wallet object for the coldkey.
286
- :param old_balance: The balance of the wallet prior to staking.
287
- :param hotkey_ss58s: List of hotkeys to stake to.
288
- :param amounts: List of amounts to stake. If `None`, stake all to the first hotkey.
289
- :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
290
- `False` if the extrinsic fails to enter the block within the timeout.
291
- :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
292
- or returns `False` if the extrinsic fails to be finalized within the timeout.
293
- :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
294
-
295
- :return: success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If
296
- we did not wait for finalization/inclusion, the response is `True`.
297
- """
298
-
299
- if len(hotkey_ss58s) == 0:
300
- return True
301
-
302
- if amounts is not None and len(amounts) != len(hotkey_ss58s):
303
- raise ValueError("amounts must be a list of the same length as hotkey_ss58s")
304
-
305
- new_amounts: Sequence[Optional[Balance]]
306
- if amounts is None:
307
- new_amounts = [None] * len(hotkey_ss58s)
308
- else:
309
- new_amounts = [Balance.from_tao(amount) for amount in amounts]
310
- if sum(amount.tao for amount in new_amounts) == 0:
311
- # Staking 0 tao
312
- return True
313
-
314
- # Decrypt coldkey.
315
- try:
316
- wallet.unlock_coldkey()
317
- except KeyFileError:
318
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
319
- return False
320
-
321
- with console.status(
322
- f":satellite: Syncing with chain: [white]{subtensor}[/white] ..."
323
- ):
324
- block_hash = await subtensor.substrate.get_chain_head()
325
- old_stakes = await asyncio.gather(
326
- *[
327
- subtensor.get_stake_for_coldkey_and_hotkey(
328
- hk, wallet.coldkeypub.ss58_address, block_hash=block_hash
329
- )
330
- for hk in hotkey_ss58s
331
- ]
332
- )
333
-
334
- # Remove existential balance to keep key alive.
335
- ## Keys must maintain a balance of at least 1000 rao to stay alive.
336
- total_staking_rao = sum(
337
- [amount.rao if amount is not None else 0 for amount in new_amounts]
338
- )
339
- if total_staking_rao == 0:
340
- # Staking all to the first wallet.
341
- if old_balance.rao > 1000:
342
- old_balance -= Balance.from_rao(1000)
343
-
344
- elif total_staking_rao < 1000:
345
- # Staking less than 1000 rao to the wallets.
346
- pass
347
- else:
348
- # Staking more than 1000 rao to the wallets.
349
- ## Reduce the amount to stake to each wallet to keep the balance above 1000 rao.
350
- percent_reduction = 1 - (1000 / total_staking_rao)
351
- new_amounts = [
352
- Balance.from_tao(amount.tao * percent_reduction)
353
- for amount in cast(Sequence[Balance], new_amounts)
354
- ]
355
-
356
- successful_stakes = 0
357
- for idx, (hotkey_ss58, amount, old_stake) in enumerate(
358
- zip(hotkey_ss58s, new_amounts, old_stakes)
359
- ):
360
- staking_all = False
361
- # Convert to bittensor.Balance
362
- if amount is None:
363
- # Stake it all.
364
- staking_balance = Balance.from_tao(old_balance.tao)
365
- staking_all = True
366
- else:
367
- # Amounts are cast to balance earlier in the function
368
- assert isinstance(amount, Balance)
369
- staking_balance = amount
370
-
371
- # Check enough to stake
372
- if staking_balance > old_balance:
373
- err_console.print(
374
- f":cross_mark: [red]Not enough balance[/red]:"
375
- f" [green]{old_balance}[/green] to stake: [blue]{staking_balance}[/blue]"
376
- f" from coldkey: [white]{wallet.name}[/white]"
377
- )
378
- continue
379
-
380
- # Ask before moving on.
381
- if prompt:
382
- if not Confirm.ask(
383
- f"Do you want to stake:\n"
384
- f"\t[bold white]amount: {staking_balance}\n"
385
- f"\thotkey: {wallet.hotkey_str}[/bold white ]?"
386
- ):
387
- continue
388
-
389
- call = await subtensor.substrate.compose_call(
390
- call_module="SubtensorModule",
391
- call_function="add_stake",
392
- call_params={"hotkey": hotkey_ss58, "amount_staked": staking_balance.rao},
393
- )
394
- staking_response, err_msg = await subtensor.sign_and_send_extrinsic(
395
- call, wallet, wait_for_inclusion, wait_for_finalization
396
- )
397
-
398
- if staking_response is True: # If we successfully staked.
399
- # We only wait here if we expect finalization.
400
-
401
- if idx < len(hotkey_ss58s) - 1:
402
- # Wait for tx rate limit.
403
- tx_query = await subtensor.substrate.query(
404
- module="SubtensorModule",
405
- storage_function="TxRateLimit",
406
- block_hash=block_hash,
407
- )
408
- tx_rate_limit_blocks: int = tx_query
409
- if tx_rate_limit_blocks > 0:
410
- with console.status(
411
- f":hourglass: [yellow]Waiting for tx rate limit:"
412
- f" [white]{tx_rate_limit_blocks}[/white] blocks[/yellow]"
413
- ):
414
- await asyncio.sleep(
415
- tx_rate_limit_blocks * 12
416
- ) # 12 seconds per block
417
-
418
- if not wait_for_finalization and not wait_for_inclusion:
419
- old_balance -= staking_balance
420
- successful_stakes += 1
421
- if staking_all:
422
- # If staked all, no need to continue
423
- break
424
-
425
- continue
426
-
427
- console.print(":white_heavy_check_mark: [green]Finalized[/green]")
428
-
429
- new_block_hash = await subtensor.substrate.get_chain_head()
430
- new_stake, new_balance_ = await asyncio.gather(
431
- subtensor.get_stake_for_coldkey_and_hotkey(
432
- coldkey_ss58=wallet.coldkeypub.ss58_address,
433
- hotkey_ss58=hotkey_ss58,
434
- block_hash=new_block_hash,
435
- ),
436
- subtensor.get_balance(
437
- wallet.coldkeypub.ss58_address, block_hash=new_block_hash
438
- ),
439
- )
440
- new_balance = new_balance_[wallet.coldkeypub.ss58_address]
441
- console.print(
442
- "Stake ({}): [blue]{}[/blue] :arrow_right: [green]{}[/green]".format(
443
- hotkey_ss58, old_stake, new_stake
444
- )
445
- )
446
- old_balance = new_balance
447
- successful_stakes += 1
448
- if staking_all:
449
- # If staked all, no need to continue
450
- break
451
-
452
- else:
453
- err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
454
- continue
455
-
456
- if successful_stakes != 0:
457
- with console.status(
458
- f":satellite: Checking Balance on: ([white]{subtensor}[/white] ..."
459
- ):
460
- new_balance_ = await subtensor.get_balance(
461
- wallet.coldkeypub.ss58_address, reuse_block=False
462
- )
463
- new_balance = new_balance_[wallet.coldkeypub.ss58_address]
464
- console.print(
465
- f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]"
466
- )
467
- return True
468
-
469
- return False
470
-
471
-
472
- async def unstake_extrinsic(
473
- subtensor: "SubtensorInterface",
474
- wallet: Wallet,
475
- hotkey_ss58: Optional[str] = None,
476
- amount: Optional[Balance] = None,
477
- wait_for_inclusion: bool = True,
478
- wait_for_finalization: bool = False,
479
- prompt: bool = False,
480
- ) -> bool:
481
- """Removes stake into the wallet coldkey from the specified hotkey ``uid``.
482
-
483
- :param subtensor: the initialized SubtensorInterface object to use
484
- :param wallet: Bittensor wallet object.
485
- :param hotkey_ss58: The `ss58` address of the hotkey to unstake from. By default, the wallet hotkey is used.
486
- :param amount: Amount to stake as Bittensor balance, or `None` is unstaking all
487
- :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
488
- `False` if the extrinsic fails to enter the block within the timeout.
489
- :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
490
- or returns `False` if the extrinsic fails to be finalized within the timeout.
491
- :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
492
-
493
- :return: success: `True` if extrinsic was finalized or included in the block. If we did not wait for
494
- finalization/inclusion, the response is `True`.
495
- """
496
- # Decrypt keys,
497
- try:
498
- wallet.unlock_coldkey()
499
- except KeyFileError:
500
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
501
- return False
502
-
503
- if hotkey_ss58 is None:
504
- hotkey_ss58 = wallet.hotkey.ss58_address # Default to wallet's own hotkey.
505
-
506
- with console.status(
507
- f":satellite: Syncing with chain: [white]{subtensor}[/white] ...",
508
- spinner="aesthetic",
509
- ) as status:
510
- print_verbose("Fetching balance and stake", status)
511
- block_hash = await subtensor.substrate.get_chain_head()
512
- old_balance, old_stake, hotkey_owner = await asyncio.gather(
513
- subtensor.get_balance(
514
- wallet.coldkeypub.ss58_address, block_hash=block_hash
515
- ),
516
- subtensor.get_stake_for_coldkey_and_hotkey(
517
- coldkey_ss58=wallet.coldkeypub.ss58_address,
518
- hotkey_ss58=hotkey_ss58,
519
- block_hash=block_hash,
520
- ),
521
- subtensor.get_hotkey_owner(hotkey_ss58, block_hash),
522
- )
523
-
524
- own_hotkey: bool = wallet.coldkeypub.ss58_address == hotkey_owner
525
-
526
- # Convert to bittensor.Balance
527
- if amount is None:
528
- # Unstake it all.
529
- unstaking_balance = old_stake
530
- else:
531
- unstaking_balance = Balance.from_tao(amount)
532
-
533
- # Check enough to unstake.
534
- stake_on_uid = old_stake
535
- if unstaking_balance > stake_on_uid:
536
- err_console.print(
537
- f":cross_mark: [red]Not enough stake[/red]: "
538
- f"[green]{stake_on_uid}[/green] to unstake: "
539
- f"[blue]{unstaking_balance}[/blue] from hotkey:"
540
- f" [white]{wallet.hotkey_str}[/white]"
541
- )
542
- return False
543
-
544
- print_verbose("Fetching threshold amount")
545
- # If nomination stake, check threshold.
546
- if not own_hotkey and not await _check_threshold_amount(
547
- subtensor=subtensor,
548
- sb=(stake_on_uid - unstaking_balance),
549
- block_hash=block_hash,
550
- ):
551
- console.print(
552
- ":warning: [yellow]This action will unstake the entire staked balance![/yellow]"
553
- )
554
- unstaking_balance = stake_on_uid
555
-
556
- # Ask before moving on.
557
- if prompt:
558
- if not Confirm.ask(
559
- f"Do you want to unstake:\n"
560
- f"[bold white]\tamount: {unstaking_balance}\n"
561
- f"\thotkey: {wallet.hotkey_str}[/bold white ]?"
562
- ):
563
- return False
564
-
565
- with console.status(
566
- f":satellite: Unstaking from chain: [white]{subtensor}[/white] ...",
567
- spinner="earth",
568
- ):
569
- call = await subtensor.substrate.compose_call(
570
- call_module="SubtensorModule",
571
- call_function="remove_stake",
572
- call_params={
573
- "hotkey": hotkey_ss58,
574
- "amount_unstaked": unstaking_balance.rao,
575
- },
576
- )
577
- staking_response, err_msg = await subtensor.sign_and_send_extrinsic(
578
- call, wallet, wait_for_inclusion, wait_for_finalization
579
- )
580
-
581
- if staking_response is True: # If we successfully unstaked.
582
- # We only wait here if we expect finalization.
583
- if not wait_for_finalization and not wait_for_inclusion:
584
- return True
585
-
586
- console.print(":white_heavy_check_mark: [green]Finalized[/green]")
587
- with console.status(
588
- f":satellite: Checking Balance on: [white]{subtensor}[/white] ..."
589
- ):
590
- new_block_hash = await subtensor.substrate.get_chain_head()
591
- new_balance, new_stake = await asyncio.gather(
592
- subtensor.get_balance(
593
- wallet.coldkeypub.ss58_address, block_hash=new_block_hash
594
- ),
595
- subtensor.get_stake_for_coldkey_and_hotkey(
596
- hotkey_ss58, wallet.coldkeypub.ss58_address, new_block_hash
597
- ),
598
- )
599
- console.print(
600
- f"Balance:\n"
601
- f" [blue]{old_balance[wallet.coldkeypub.ss58_address]}[/blue] :arrow_right:"
602
- f" [green]{new_balance[wallet.coldkeypub.ss58_address]}[/green]"
603
- )
604
- console.print(
605
- f"Stake:\n [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]"
606
- )
607
- return True
608
- else:
609
- err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
610
- return False
611
-
612
-
613
- async def unstake_multiple_extrinsic(
614
- subtensor: "SubtensorInterface",
615
- wallet: Wallet,
616
- hotkey_ss58s: list[str],
617
- amounts: Optional[list[Union[Balance, float]]] = None,
618
- wait_for_inclusion: bool = True,
619
- wait_for_finalization: bool = False,
620
- prompt: bool = False,
621
- ) -> bool:
622
- """
623
- Removes stake from each `hotkey_ss58` in the list, using each amount, to a common coldkey.
624
-
625
- :param subtensor: the initialized SubtensorInterface object to use
626
- :param wallet: The wallet with the coldkey to unstake to.
627
- :param hotkey_ss58s: List of hotkeys to unstake from.
628
- :param amounts: List of amounts to unstake. If ``None``, unstake all.
629
- :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
630
- `False` if the extrinsic fails to enter the block within the timeout.
631
- :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
632
- or returns `False` if the extrinsic fails to be finalized within the timeout.
633
- :param prompt: If `True`, the call waits for confirmation from the user before proceeding.
634
-
635
- :return: success: `True` if extrinsic was finalized or included in the block. Flag is `True` if any wallet was
636
- unstaked. If we did not wait for finalization/inclusion, the response is `True`.
637
- """
638
- if not isinstance(hotkey_ss58s, list) or not all(
639
- isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s
640
- ):
641
- raise TypeError("hotkey_ss58s must be a list of str")
642
-
643
- if len(hotkey_ss58s) == 0:
644
- return True
645
-
646
- if amounts is not None and len(amounts) != len(hotkey_ss58s):
647
- raise ValueError("amounts must be a list of the same length as hotkey_ss58s")
648
-
649
- if amounts is not None and not all(
650
- isinstance(amount, (Balance, float)) for amount in amounts
651
- ):
652
- raise TypeError(
653
- "amounts must be a [list of bittensor.Balance or float] or None"
654
- )
655
-
656
- new_amounts: Sequence[Optional[Balance]]
657
- if amounts is None:
658
- new_amounts = [None] * len(hotkey_ss58s)
659
- else:
660
- new_amounts = [
661
- Balance(amount) if not isinstance(amount, Balance) else amount
662
- for amount in (amounts or [None] * len(hotkey_ss58s))
663
- ]
664
- if sum(amount.tao for amount in new_amounts if amount is not None) == 0:
665
- return True
666
-
667
- # Unlock coldkey.
668
- try:
669
- wallet.unlock_coldkey()
670
- except KeyFileError:
671
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
672
- return False
673
-
674
- with console.status(
675
- f":satellite: Syncing with chain: [white]{subtensor}[/white] ..."
676
- ):
677
- block_hash = await subtensor.substrate.get_chain_head()
678
-
679
- old_balance_ = subtensor.get_balance(
680
- wallet.coldkeypub.ss58_address, block_hash=block_hash
681
- )
682
- old_stakes_ = asyncio.gather(
683
- *[
684
- subtensor.get_stake_for_coldkey_and_hotkey(
685
- h, wallet.coldkeypub.ss58_address, block_hash
686
- )
687
- for h in hotkey_ss58s
688
- ]
689
- )
690
- hotkey_owners_ = asyncio.gather(
691
- *[subtensor.get_hotkey_owner(h, block_hash) for h in hotkey_ss58s]
692
- )
693
-
694
- old_balance, old_stakes, hotkey_owners, threshold = await asyncio.gather(
695
- old_balance_,
696
- old_stakes_,
697
- hotkey_owners_,
698
- _get_threshold_amount(subtensor, block_hash),
699
- )
700
- own_hotkeys = [
701
- wallet.coldkeypub.ss58_address == hotkey_owner
702
- for hotkey_owner in hotkey_owners
703
- ]
704
-
705
- successful_unstakes = 0
706
- for idx, (hotkey_ss58, amount, old_stake, own_hotkey) in enumerate(
707
- zip(hotkey_ss58s, new_amounts, old_stakes, own_hotkeys)
708
- ):
709
- # Covert to bittensor.Balance
710
- if amount is None:
711
- # Unstake it all.
712
- unstaking_balance = old_stake
713
- else:
714
- unstaking_balance = amount
715
-
716
- # Check enough to unstake.
717
- stake_on_uid = old_stake
718
- if unstaking_balance > stake_on_uid:
719
- err_console.print(
720
- f":cross_mark: [red]Not enough stake[/red]:"
721
- f" [green]{stake_on_uid}[/green] to unstake:"
722
- f" [blue]{unstaking_balance}[/blue] from hotkey:"
723
- f" [white]{wallet.hotkey_str}[/white]"
724
- )
725
- continue
726
-
727
- # If nomination stake, check threshold.
728
- if (
729
- not own_hotkey
730
- and (
731
- await _check_threshold_amount(
732
- subtensor=subtensor,
733
- sb=(stake_on_uid - unstaking_balance),
734
- block_hash=block_hash,
735
- min_req_stake=threshold,
736
- )
737
- )[0]
738
- is False
739
- ):
740
- console.print(
741
- ":warning: [yellow]This action will unstake the entire staked balance![/yellow]"
742
- )
743
- unstaking_balance = stake_on_uid
744
-
745
- # Ask before moving on.
746
- if prompt:
747
- if not Confirm.ask(
748
- f"Do you want to unstake:\n"
749
- f"[bold white]\tamount: {unstaking_balance}\n"
750
- f"ss58: {hotkey_ss58}[/bold white ]?"
751
- ):
752
- continue
753
-
754
- with console.status(
755
- f":satellite: Unstaking from chain: [white]{subtensor}[/white] ..."
756
- ):
757
- call = await subtensor.substrate.compose_call(
758
- call_module="SubtensorModule",
759
- call_function="remove_stake",
760
- call_params={
761
- "hotkey": hotkey_ss58,
762
- "amount_unstaked": unstaking_balance.rao,
763
- },
764
- )
765
- staking_response, err_msg = await subtensor.sign_and_send_extrinsic(
766
- call, wallet, wait_for_inclusion, wait_for_finalization
767
- )
768
-
769
- if staking_response is True: # If we successfully unstaked.
770
- # We only wait here if we expect finalization.
771
-
772
- if idx < len(hotkey_ss58s) - 1:
773
- # Wait for tx rate limit.
774
- tx_query = await subtensor.substrate.query(
775
- module="SubtensorModule",
776
- storage_function="TxRateLimit",
777
- block_hash=block_hash,
778
- )
779
- tx_rate_limit_blocks: int = tx_query
780
-
781
- # TODO: Handle in-case we have fast blocks
782
- if tx_rate_limit_blocks > 0:
783
- console.print(
784
- ":hourglass: [yellow]Waiting for tx rate limit:"
785
- f" [white]{tx_rate_limit_blocks}[/white] blocks,"
786
- f" estimated time: [white]{tx_rate_limit_blocks * 12} [/white] seconds[/yellow]"
787
- )
788
- await asyncio.sleep(
789
- tx_rate_limit_blocks * 12
790
- ) # 12 seconds per block
791
-
792
- if not wait_for_finalization and not wait_for_inclusion:
793
- successful_unstakes += 1
794
- continue
795
-
796
- console.print(":white_heavy_check_mark: [green]Finalized[/green]")
797
- with console.status(
798
- f":satellite: Checking stake balance on: [white]{subtensor}[/white] ..."
799
- ):
800
- new_stake = await subtensor.get_stake_for_coldkey_and_hotkey(
801
- coldkey_ss58=wallet.coldkeypub.ss58_address,
802
- hotkey_ss58=hotkey_ss58,
803
- block_hash=(await subtensor.substrate.get_chain_head()),
804
- )
805
- console.print(
806
- "Stake ({}): [blue]{}[/blue] :arrow_right: [green]{}[/green]".format(
807
- hotkey_ss58, stake_on_uid, new_stake
808
- )
809
- )
810
- successful_unstakes += 1
811
- else:
812
- err_console.print(":cross_mark: [red]Failed[/red]: Unknown Error.")
813
- continue
814
-
815
- if successful_unstakes != 0:
816
- with console.status(
817
- f":satellite: Checking balance on: ([white]{subtensor}[/white] ..."
818
- ):
819
- new_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
820
- console.print(
821
- f"Balance: [blue]{old_balance[wallet.coldkeypub.ss58_address]}[/blue]"
822
- f" :arrow_right: [green]{new_balance[wallet.coldkeypub.ss58_address]}[/green]"
823
- )
824
- return True
825
-
826
- return False
827
-
828
-
829
- # Commands
830
38
  async def stake_add(
831
39
  wallet: Wallet,
832
40
  subtensor: "SubtensorInterface",
@@ -931,8 +139,8 @@ async def stake_add(
931
139
 
932
140
  starting_chain_head = await subtensor.substrate.get_chain_head()
933
141
  _all_dynamic_info, stake_info_dict = await asyncio.gather(
934
- subtensor.get_all_subnet_dynamic_info(),
935
- subtensor.get_stake_info_for_coldkeys(
142
+ subtensor.all_subnets(),
143
+ subtensor.get_stake_for_coldkeys(
936
144
  coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
937
145
  block_hash=starting_chain_head,
938
146
  ),
@@ -998,15 +206,10 @@ async def stake_add(
998
206
  remaining_wallet_balance -= amount_to_stake_as_balance
999
207
 
1000
208
  # Slippage warning
1001
- received_amount, slippage = dynamic_info.tao_to_alpha_with_slippage(
1002
- amount_to_stake_as_balance
209
+ received_amount, _, slippage_pct_float = (
210
+ dynamic_info.tao_to_alpha_with_slippage(amount_to_stake_as_balance)
1003
211
  )
1004
212
  if dynamic_info.is_dynamic:
1005
- slippage_pct_float = (
1006
- 100 * float(slippage) / float(slippage + received_amount)
1007
- if slippage + received_amount != 0
1008
- else 0
1009
- )
1010
213
  slippage_pct = f"{slippage_pct_float:.4f} %"
1011
214
  rate = str(1 / (float(dynamic_info.price) or 1))
1012
215
  else:
@@ -1114,7 +317,7 @@ The columns are as follows:
1114
317
  else:
1115
318
  new_balance_, stake_info_dict = await asyncio.gather(
1116
319
  subtensor.get_balance(wallet.coldkeypub.ss58_address),
1117
- subtensor.get_stake_info_for_coldkeys(
320
+ subtensor.get_stake_for_coldkeys(
1118
321
  coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
1119
322
  ),
1120
323
  )
@@ -1179,7 +382,7 @@ async def unstake_selection(
1179
382
  old_identities,
1180
383
  netuid: Optional[int] = None,
1181
384
  ):
1182
- stake_infos = await subtensor.get_stake_info_for_coldkey(
385
+ stake_infos = await subtensor.get_stake_for_coldkey(
1183
386
  coldkey_ss58=wallet.coldkeypub.ss58_address
1184
387
  )
1185
388
 
@@ -1407,10 +610,10 @@ async def _unstake_all(
1407
610
  all_sn_dynamic_info_,
1408
611
  current_wallet_balance,
1409
612
  ) = await asyncio.gather(
1410
- subtensor.get_stake_info_for_coldkey(wallet.coldkeypub.ss58_address),
613
+ subtensor.get_stake_for_coldkey(wallet.coldkeypub.ss58_address),
1411
614
  subtensor.fetch_coldkey_hotkey_identities(),
1412
615
  subtensor.get_delegate_identities(),
1413
- subtensor.get_all_subnet_dynamic_info(),
616
+ subtensor.all_subnets(),
1414
617
  subtensor.get_balance(wallet.coldkeypub.ss58_address),
1415
618
  )
1416
619
 
@@ -1472,8 +675,8 @@ async def _unstake_all(
1472
675
 
1473
676
  dynamic_info = all_sn_dynamic_info.get(stake.netuid)
1474
677
  stake_amount = stake.stake
1475
- received_amount, slippage = dynamic_info.alpha_to_tao_with_slippage(
1476
- stake_amount
678
+ received_amount, _, slippage_pct_float = (
679
+ dynamic_info.alpha_to_tao_with_slippage(stake_amount)
1477
680
  )
1478
681
 
1479
682
  total_received_value += received_amount
@@ -1491,11 +694,6 @@ async def _unstake_all(
1491
694
  hotkey_display = stake.hotkey_ss58
1492
695
 
1493
696
  if dynamic_info.is_dynamic:
1494
- slippage_pct_float = (
1495
- 100 * float(slippage) / float(slippage + received_amount)
1496
- if slippage + received_amount != 0
1497
- else 0
1498
- )
1499
697
  slippage_pct = f"{slippage_pct_float:.4f} %"
1500
698
  else:
1501
699
  slippage_pct_float = 0
@@ -1600,7 +798,7 @@ async def unstake(
1600
798
  spinner="earth",
1601
799
  ):
1602
800
  all_sn_dynamic_info_, ck_hk_identities, old_identities = await asyncio.gather(
1603
- subtensor.get_all_subnet_dynamic_info(),
801
+ subtensor.all_subnets(),
1604
802
  subtensor.fetch_coldkey_hotkey_identities(),
1605
803
  subtensor.get_delegate_identities(),
1606
804
  )
@@ -1684,7 +882,7 @@ async def unstake(
1684
882
 
1685
883
  # Fetch stake balances
1686
884
  chain_head = await subtensor.substrate.get_chain_head()
1687
- stake_info_dict = await subtensor.get_stake_info_for_coldkeys(
885
+ stake_info_dict = await subtensor.get_stake_for_coldkeys(
1688
886
  coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
1689
887
  block_hash=chain_head,
1690
888
  )
@@ -1765,16 +963,11 @@ async def unstake(
1765
963
  )
1766
964
  continue # Skip to the next subnet - useful when single amount is specified for all subnets
1767
965
 
1768
- received_amount, slippage = dynamic_info.alpha_to_tao_with_slippage(
1769
- amount_to_unstake_as_balance
966
+ received_amount, _, slippage_pct_float = (
967
+ dynamic_info.alpha_to_tao_with_slippage(amount_to_unstake_as_balance)
1770
968
  )
1771
969
  total_received_amount += received_amount
1772
970
  if dynamic_info.is_dynamic:
1773
- slippage_pct_float = (
1774
- 100 * float(slippage) / float(slippage + received_amount)
1775
- if slippage + received_amount != 0
1776
- else 0
1777
- )
1778
971
  slippage_pct = f"{slippage_pct_float:.4f} %"
1779
972
  else:
1780
973
  slippage_pct_float = 0
@@ -1961,7 +1154,7 @@ The columns are as follows:
1961
1154
  wallet.coldkeypub.ss58_address
1962
1155
  )
1963
1156
  new_balance = new_balance_[wallet.coldkeypub.ss58_address]
1964
- new_stake_info = await subtensor.get_stake_info_for_coldkeys(
1157
+ new_stake_info = await subtensor.get_stake_for_coldkeys(
1965
1158
  coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
1966
1159
  )
1967
1160
  new_stake = Balance.from_rao(0)
@@ -2001,11 +1194,11 @@ async def stake_list(
2001
1194
  registered_delegate_info,
2002
1195
  _dynamic_info,
2003
1196
  ) = await asyncio.gather(
2004
- subtensor.get_stake_info_for_coldkeys(
1197
+ subtensor.get_stake_for_coldkeys(
2005
1198
  coldkey_ss58_list=[coldkey_address], block_hash=block_hash
2006
1199
  ),
2007
1200
  subtensor.get_delegate_identities(block_hash=block_hash),
2008
- subtensor.get_all_subnet_dynamic_info(),
1201
+ subtensor.all_subnets(),
2009
1202
  )
2010
1203
  sub_stakes = substakes[coldkey_address]
2011
1204
  dynamic_info = {info.netuid: info for info in _dynamic_info}
@@ -2131,18 +1324,13 @@ async def stake_list(
2131
1324
  total_tao_value += tao_value
2132
1325
 
2133
1326
  # Swapped TAO value and slippage cell
2134
- swapped_tao_value, slippage = pool.alpha_to_tao_with_slippage(
2135
- substake_.stake
1327
+ swapped_tao_value, _, slippage_percentage_ = (
1328
+ pool.alpha_to_tao_with_slippage(substake_.stake)
2136
1329
  )
2137
1330
  total_swapped_tao_value += swapped_tao_value
2138
1331
 
2139
1332
  # Slippage percentage cell
2140
1333
  if pool.is_dynamic:
2141
- slippage_percentage_ = (
2142
- 100 * float(slippage) / float(slippage + swapped_tao_value)
2143
- if slippage + swapped_tao_value != 0
2144
- else 0
2145
- )
2146
1334
  slippage_percentage = f"[{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}]{slippage_percentage_:.3f}%[/{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}]"
2147
1335
  else:
2148
1336
  slippage_percentage = f"[{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}]0.000%[/{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}]"
@@ -2648,305 +1836,3 @@ async def stake_list(
2648
1836
  for field_name, description in fields:
2649
1837
  description_table.add_row(field_name, description)
2650
1838
  console.print(description_table)
2651
-
2652
-
2653
- async def move_stake(
2654
- subtensor: "SubtensorInterface",
2655
- wallet: Wallet,
2656
- origin_netuid: int,
2657
- destination_netuid: int,
2658
- destination_hotkey: str,
2659
- amount: float,
2660
- stake_all: bool,
2661
- prompt: bool = True,
2662
- ):
2663
- origin_hotkey_ss58 = wallet.hotkey.ss58_address
2664
- # Get the wallet stake balances.
2665
- origin_stake_balance = Balance.from_rao(0)
2666
- destination_stake_balance = Balance.from_rao(0)
2667
-
2668
- chain_head = await subtensor.substrate.get_chain_head()
2669
- stake_info_dict = await subtensor.get_stake_info_for_coldkeys(
2670
- coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
2671
- block_hash=chain_head,
2672
- )
2673
-
2674
- for stake_info in stake_info_dict[wallet.coldkeypub.ss58_address]:
2675
- if (
2676
- stake_info.hotkey_ss58 == origin_hotkey_ss58
2677
- and stake_info.netuid == origin_netuid
2678
- ):
2679
- origin_stake_balance = stake_info.stake
2680
- elif (
2681
- stake_info.hotkey_ss58 == destination_hotkey
2682
- and stake_info.netuid == destination_netuid
2683
- ):
2684
- destination_stake_balance = stake_info.stake
2685
-
2686
- # Set appropriate units
2687
- origin_stake_balance = origin_stake_balance.set_unit(origin_netuid)
2688
- destination_stake_balance = destination_stake_balance.set_unit(destination_netuid)
2689
-
2690
- if origin_stake_balance == Balance.from_tao(0).set_unit(origin_netuid):
2691
- print_error(
2692
- f"Your balance is [{COLOR_PALETTE['POOLS']['TAO']}]0[/{COLOR_PALETTE['POOLS']['TAO']}] in Netuid: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{origin_netuid}"
2693
- )
2694
- raise typer.Exit()
2695
-
2696
- console.print(
2697
- f"\nOrigin Netuid: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{origin_netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}], Origin stake: [{COLOR_PALETTE['POOLS']['TAO']}]{origin_stake_balance}"
2698
- )
2699
- console.print(
2700
- f"Destination netuid: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{destination_netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}], Destination stake: [{COLOR_PALETTE['POOLS']['TAO']}]{destination_stake_balance}\n"
2701
- )
2702
-
2703
- # Determine the amount we are moving.
2704
- amount_to_move_as_balance = None
2705
- if amount:
2706
- amount_to_move_as_balance = Balance.from_tao(amount)
2707
- elif stake_all:
2708
- amount_to_move_as_balance = origin_stake_balance
2709
- else: # max_stake
2710
- # TODO improve this
2711
- if Confirm.ask(
2712
- f"Move all: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{origin_stake_balance}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]?"
2713
- ):
2714
- amount_to_move_as_balance = origin_stake_balance
2715
- else:
2716
- try:
2717
- amount = float(
2718
- Prompt.ask(
2719
- f"Enter amount to move in [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{Balance.get_unit(origin_netuid)}"
2720
- )
2721
- )
2722
- amount_to_move_as_balance = Balance.from_tao(amount)
2723
- except ValueError:
2724
- print_error(f":cross_mark: Invalid amount: {amount}")
2725
- return False
2726
-
2727
- # Check enough to move.
2728
- amount_to_move_as_balance.set_unit(origin_netuid)
2729
- if amount_to_move_as_balance > origin_stake_balance:
2730
- err_console.print(
2731
- f"[red]Not enough stake[/red]:\n Stake balance: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{origin_stake_balance}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] < Moving amount: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_to_move_as_balance}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]"
2732
- )
2733
- return False
2734
-
2735
- # Slippage warning
2736
- if prompt:
2737
- if origin_netuid == destination_netuid:
2738
- received_amount_destination = amount_to_move_as_balance
2739
- slippage_pct_float = 0
2740
- slippage_pct = f"{slippage_pct_float}%"
2741
- price = Balance.from_tao(1).set_unit(origin_netuid)
2742
- price_str = (
2743
- str(float(price.tao))
2744
- + f"{Balance.get_unit(origin_netuid)}/{Balance.get_unit(origin_netuid)}"
2745
- )
2746
- else:
2747
- dynamic_origin, dynamic_destination = await asyncio.gather(
2748
- subtensor.get_subnet_dynamic_info(origin_netuid),
2749
- subtensor.get_subnet_dynamic_info(destination_netuid),
2750
- )
2751
- price = (
2752
- float(dynamic_origin.price)
2753
- * 1
2754
- / (float(dynamic_destination.price) or 1)
2755
- )
2756
- received_amount_tao, slippage = dynamic_origin.alpha_to_tao_with_slippage(
2757
- amount_to_move_as_balance
2758
- )
2759
- received_amount_destination, slippage = (
2760
- dynamic_destination.tao_to_alpha_with_slippage(received_amount_tao)
2761
- )
2762
- received_amount_destination.set_unit(destination_netuid)
2763
- slippage_pct_float = (
2764
- 100 * float(slippage) / float(slippage + received_amount_destination)
2765
- if slippage + received_amount_destination != 0
2766
- else 0
2767
- )
2768
- slippage_pct = f"{slippage_pct_float:.4f} %"
2769
- price_str = (
2770
- str(float(price))
2771
- + f"{Balance.get_unit(destination_netuid)}/{Balance.get_unit(origin_netuid)}"
2772
- )
2773
-
2774
- table = Table(
2775
- title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Moving stake from: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{Balance.get_unit(origin_netuid)}(Netuid: {origin_netuid})[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] to: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{Balance.get_unit(destination_netuid)}(Netuid: {destination_netuid})[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}]\nNetwork: {subtensor.network}\n",
2776
- show_footer=True,
2777
- show_edge=False,
2778
- header_style="bold white",
2779
- border_style="bright_black",
2780
- style="bold",
2781
- title_justify="center",
2782
- show_lines=False,
2783
- pad_edge=True,
2784
- )
2785
- table.add_column(
2786
- "origin netuid", justify="center", style=COLOR_PALETTE["GENERAL"]["SYMBOL"]
2787
- )
2788
- table.add_column(
2789
- "origin hotkey", justify="center", style=COLOR_PALETTE["GENERAL"]["HOTKEY"]
2790
- )
2791
- table.add_column(
2792
- "dest netuid", justify="center", style=COLOR_PALETTE["GENERAL"]["SYMBOL"]
2793
- )
2794
- table.add_column(
2795
- "dest hotkey", justify="center", style=COLOR_PALETTE["GENERAL"]["HOTKEY"]
2796
- )
2797
- table.add_column(
2798
- f"amount ({Balance.get_unit(origin_netuid)})",
2799
- justify="center",
2800
- style=COLOR_PALETTE["STAKE"]["TAO"],
2801
- )
2802
- table.add_column(
2803
- f"rate ({Balance.get_unit(destination_netuid)}/{Balance.get_unit(origin_netuid)})",
2804
- justify="center",
2805
- style=COLOR_PALETTE["POOLS"]["RATE"],
2806
- )
2807
- table.add_column(
2808
- f"received ({Balance.get_unit(destination_netuid)})",
2809
- justify="center",
2810
- style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"],
2811
- )
2812
- table.add_column(
2813
- "slippage",
2814
- justify="center",
2815
- style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"],
2816
- )
2817
-
2818
- table.add_row(
2819
- f"{Balance.get_unit(origin_netuid)}({origin_netuid})",
2820
- f"{origin_hotkey_ss58[:3]}...{origin_hotkey_ss58[-3:]}",
2821
- # TODO f-strings
2822
- Balance.get_unit(destination_netuid) + "(" + str(destination_netuid) + ")",
2823
- f"{destination_hotkey[:3]}...{destination_hotkey[-3:]}",
2824
- str(amount_to_move_as_balance),
2825
- price_str,
2826
- str(received_amount_destination.set_unit(destination_netuid)),
2827
- str(slippage_pct),
2828
- )
2829
-
2830
- console.print(table)
2831
- message = ""
2832
- if slippage_pct_float > 5:
2833
- message += f"[{COLOR_PALETTE['STAKE']['SLIPPAGE_TEXT']}]-------------------------------------------------------------------------------------------------------------------\n"
2834
- message += f"[bold]WARNING:\tSlippage is high: [{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}]{slippage_pct}[/{COLOR_PALETTE['STAKE']['SLIPPAGE_PERCENT']}], this may result in a loss of funds.[/bold] \n"
2835
- message += "-------------------------------------------------------------------------------------------------------------------\n"
2836
- console.print(message)
2837
- if not Confirm.ask("Would you like to continue?"):
2838
- return True
2839
-
2840
- # Perform staking operation.
2841
- try:
2842
- wallet.unlock_coldkey()
2843
- except KeyFileError:
2844
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
2845
- return False
2846
- with console.status(
2847
- f"\n:satellite: Moving {amount_to_move_as_balance} from {origin_hotkey_ss58} on netuid: {origin_netuid} to "
2848
- f"{destination_hotkey} on netuid: {destination_netuid} ..."
2849
- ):
2850
- call = await subtensor.substrate.compose_call(
2851
- call_module="SubtensorModule",
2852
- call_function="move_stake",
2853
- call_params={
2854
- "origin_hotkey": origin_hotkey_ss58,
2855
- "origin_netuid": origin_netuid,
2856
- "destination_hotkey": destination_hotkey,
2857
- "destination_netuid": destination_netuid,
2858
- "alpha_amount": amount_to_move_as_balance.rao,
2859
- },
2860
- )
2861
- extrinsic = await subtensor.substrate.create_signed_extrinsic(
2862
- call=call, keypair=wallet.coldkey
2863
- )
2864
- response = await subtensor.substrate.submit_extrinsic(
2865
- extrinsic, wait_for_inclusion=True, wait_for_finalization=False
2866
- )
2867
- if not prompt:
2868
- console.print(":white_heavy_check_mark: [green]Sent[/green]")
2869
- return True
2870
- else:
2871
- await response.process_events()
2872
- if not await response.is_success:
2873
- err_console.print(
2874
- f":cross_mark: [red]Failed[/red] with error:"
2875
- f" {format_error_message(response.error_message, subtensor.substrate)}"
2876
- )
2877
- return
2878
- else:
2879
- new_stake_info_dict = await subtensor.get_stake_info_for_coldkeys(
2880
- coldkey_ss58_list=[wallet.coldkeypub.ss58_address],
2881
- )
2882
-
2883
- new_origin_stake_balance = Balance.from_rao(0)
2884
- new_destination_stake_balance = Balance.from_rao(0)
2885
-
2886
- for stake_info in new_stake_info_dict[wallet.coldkeypub.ss58_address]:
2887
- if (
2888
- stake_info.hotkey_ss58 == origin_hotkey_ss58
2889
- and stake_info.netuid == origin_netuid
2890
- ):
2891
- new_origin_stake_balance = stake_info.stake.set_unit(
2892
- origin_netuid
2893
- )
2894
- elif (
2895
- stake_info.hotkey_ss58 == destination_hotkey
2896
- and stake_info.netuid == destination_netuid
2897
- ):
2898
- new_destination_stake_balance = stake_info.stake.set_unit(
2899
- destination_netuid
2900
- )
2901
-
2902
- console.print(
2903
- f"Origin Stake:\n [blue]{origin_stake_balance}[/blue] :arrow_right: "
2904
- f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_origin_stake_balance}"
2905
- )
2906
- console.print(
2907
- f"Destination Stake:\n [blue]{destination_stake_balance}[/blue] :arrow_right: "
2908
- f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_destination_stake_balance}"
2909
- )
2910
- return
2911
-
2912
-
2913
- async def fetch_coldkey_stake(subtensor: "SubtensorInterface", wallet: Wallet):
2914
- sub_stakes = await subtensor.get_stake_info_for_coldkey(
2915
- coldkey_ss58=wallet.coldkeypub.ss58_address
2916
- )
2917
- return sub_stakes
2918
-
2919
-
2920
- # TODO: Use this in all subnet commands.
2921
- async def get_stake_info_for_coldkey_and_hotkey(
2922
- subtensor: "SubtensorInterface",
2923
- coldkey_ss58: str,
2924
- hotkey_ss58: Optional[str] = None,
2925
- netuid: Optional[int] = None,
2926
- block_hash: Optional[str] = None,
2927
- ) -> dict[tuple[str, int], Balance]:
2928
- """Helper function to get stake info for a coldkey and optionally filter by hotkey and netuid.
2929
-
2930
- Args:
2931
- subtensor: SubtensorInterface instance
2932
- coldkey_ss58: Coldkey SS58 address
2933
- hotkey_ss58: Optional hotkey SS58 address to filter by
2934
- netuid: Optional netuid to filter by
2935
- block_hash: Optional block hash to query at
2936
-
2937
- Returns:
2938
- Dictionary mapping (hotkey, netuid) tuple to stake balance
2939
- """
2940
- stake_info_dict = await subtensor.get_stake_info_for_coldkeys(
2941
- coldkey_ss58_list=[coldkey_ss58], block_hash=block_hash
2942
- )
2943
-
2944
- stakes = {}
2945
- for stake_info in stake_info_dict[coldkey_ss58]:
2946
- if hotkey_ss58 and stake_info.hotkey_ss58 != hotkey_ss58:
2947
- continue
2948
- if netuid is not None and stake_info.netuid != netuid:
2949
- continue
2950
- stakes[(stake_info.hotkey_ss58, stake_info.netuid)] = stake_info.stake
2951
-
2952
- return stakes