bittensor-cli 9.1.4__py3-none-any.whl → 9.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bittensor_cli/cli.py +525 -131
- bittensor_cli/src/__init__.py +444 -465
- bittensor_cli/src/bittensor/chain_data.py +6 -2
- bittensor_cli/src/bittensor/extrinsics/registration.py +47 -23
- bittensor_cli/src/bittensor/extrinsics/root.py +10 -11
- bittensor_cli/src/bittensor/extrinsics/transfer.py +5 -3
- bittensor_cli/src/bittensor/subtensor_interface.py +125 -5
- bittensor_cli/src/bittensor/utils.py +4 -1
- bittensor_cli/src/commands/stake/add.py +169 -108
- bittensor_cli/src/commands/stake/children_hotkeys.py +120 -79
- bittensor_cli/src/commands/stake/list.py +54 -20
- bittensor_cli/src/commands/stake/move.py +58 -18
- bittensor_cli/src/commands/stake/remove.py +174 -92
- bittensor_cli/src/commands/subnets/price.py +11 -9
- bittensor_cli/src/commands/subnets/subnets.py +223 -80
- bittensor_cli/src/commands/sudo.py +76 -22
- bittensor_cli/src/commands/wallets.py +656 -40
- bittensor_cli/src/commands/weights.py +21 -11
- bittensor_cli/version.py +2 -1
- {bittensor_cli-9.1.4.dist-info → bittensor_cli-9.3.0.dist-info}/METADATA +30 -11
- bittensor_cli-9.3.0.dist-info/RECORD +35 -0
- {bittensor_cli-9.1.4.dist-info → bittensor_cli-9.3.0.dist-info}/WHEEL +1 -1
- bittensor_cli-9.1.4.dist-info/RECORD +0 -35
- {bittensor_cli-9.1.4.dist-info → bittensor_cli-9.3.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.1.4.dist-info → bittensor_cli-9.3.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,6 @@
|
|
1
1
|
import asyncio
|
2
|
+
import json
|
3
|
+
from collections import defaultdict
|
2
4
|
from functools import partial
|
3
5
|
|
4
6
|
from typing import TYPE_CHECKING, Optional
|
@@ -17,6 +19,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
17
19
|
print_error,
|
18
20
|
print_verbose,
|
19
21
|
unlock_key,
|
22
|
+
json_console,
|
20
23
|
)
|
21
24
|
from bittensor_wallet import Wallet
|
22
25
|
|
@@ -38,6 +41,8 @@ async def stake_add(
|
|
38
41
|
safe_staking: bool,
|
39
42
|
rate_tolerance: float,
|
40
43
|
allow_partial_stake: bool,
|
44
|
+
json_output: bool,
|
45
|
+
era: int,
|
41
46
|
):
|
42
47
|
"""
|
43
48
|
Args:
|
@@ -53,6 +58,8 @@ async def stake_add(
|
|
53
58
|
safe_staking: whether to use safe staking
|
54
59
|
rate_tolerance: rate tolerance percentage for stake operations
|
55
60
|
allow_partial_stake: whether to allow partial stake
|
61
|
+
json_output: whether to output stake info in JSON format
|
62
|
+
era: Blocks for which the transaction should be valid.
|
56
63
|
|
57
64
|
Returns:
|
58
65
|
bool: True if stake operation is successful, False otherwise
|
@@ -65,28 +72,31 @@ async def stake_add(
|
|
65
72
|
hotkey_ss58_: str,
|
66
73
|
price_limit: Balance,
|
67
74
|
status=None,
|
68
|
-
) ->
|
75
|
+
) -> bool:
|
69
76
|
err_out = partial(print_error, status=status)
|
70
77
|
failure_prelude = (
|
71
78
|
f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}"
|
72
79
|
)
|
73
|
-
current_balance = await
|
74
|
-
|
75
|
-
wallet.coldkeypub.ss58_address
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
80
|
+
current_balance, next_nonce, call = await asyncio.gather(
|
81
|
+
subtensor.get_balance(wallet.coldkeypub.ss58_address),
|
82
|
+
subtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
|
83
|
+
subtensor.substrate.compose_call(
|
84
|
+
call_module="SubtensorModule",
|
85
|
+
call_function="add_stake_limit",
|
86
|
+
call_params={
|
87
|
+
"hotkey": hotkey_ss58_,
|
88
|
+
"netuid": netuid_,
|
89
|
+
"amount_staked": amount_.rao,
|
90
|
+
"limit_price": price_limit,
|
91
|
+
"allow_partial": allow_partial_stake,
|
92
|
+
},
|
93
|
+
),
|
87
94
|
)
|
88
95
|
extrinsic = await subtensor.substrate.create_signed_extrinsic(
|
89
|
-
call=call,
|
96
|
+
call=call,
|
97
|
+
keypair=wallet.coldkey,
|
98
|
+
nonce=next_nonce,
|
99
|
+
era={"period": era},
|
90
100
|
)
|
91
101
|
try:
|
92
102
|
response = await subtensor.substrate.submit_extrinsic(
|
@@ -100,73 +110,80 @@ async def stake_add(
|
|
100
110
|
f"Either increase price tolerance or enable partial staking.",
|
101
111
|
status=status,
|
102
112
|
)
|
103
|
-
return
|
113
|
+
return False
|
104
114
|
else:
|
105
115
|
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
|
106
|
-
return
|
116
|
+
return False
|
117
|
+
if not await response.is_success:
|
118
|
+
err_out(
|
119
|
+
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
|
120
|
+
)
|
121
|
+
return False
|
107
122
|
else:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
)
|
130
|
-
|
131
|
-
amount_staked = current_balance - new_balance
|
132
|
-
if allow_partial_stake and (amount_staked != amount_):
|
133
|
-
console.print(
|
134
|
-
"Partial stake transaction. Staked:\n"
|
135
|
-
f" [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_staked}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] "
|
136
|
-
f"instead of "
|
137
|
-
f"[blue]{amount_}[/blue]"
|
138
|
-
)
|
123
|
+
if json_output:
|
124
|
+
# the rest of this checking is not necessary if using json_output
|
125
|
+
return True
|
126
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
127
|
+
new_balance, new_stake = await asyncio.gather(
|
128
|
+
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
|
129
|
+
subtensor.get_stake(
|
130
|
+
hotkey_ss58=hotkey_ss58_,
|
131
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
132
|
+
netuid=netuid_,
|
133
|
+
block_hash=block_hash,
|
134
|
+
),
|
135
|
+
)
|
136
|
+
console.print(
|
137
|
+
f":white_heavy_check_mark: [dark_sea_green3]Finalized. "
|
138
|
+
f"Stake added to netuid: {netuid_}[/dark_sea_green3]"
|
139
|
+
)
|
140
|
+
console.print(
|
141
|
+
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
|
142
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
|
143
|
+
)
|
139
144
|
|
145
|
+
amount_staked = current_balance - new_balance
|
146
|
+
if allow_partial_stake and (amount_staked != amount_):
|
140
147
|
console.print(
|
141
|
-
|
142
|
-
f"
|
143
|
-
f"
|
144
|
-
f"
|
145
|
-
f"[{
|
148
|
+
"Partial stake transaction. Staked:\n"
|
149
|
+
f" [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_staked}"
|
150
|
+
f"[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] "
|
151
|
+
f"instead of "
|
152
|
+
f"[blue]{amount_}[/blue]"
|
146
153
|
)
|
147
154
|
|
155
|
+
console.print(
|
156
|
+
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
|
157
|
+
f"{netuid_}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
|
158
|
+
f"Stake:\n"
|
159
|
+
f" [blue]{current_stake}[/blue] "
|
160
|
+
f":arrow_right: "
|
161
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
|
162
|
+
)
|
163
|
+
return True
|
164
|
+
|
148
165
|
async def stake_extrinsic(
|
149
166
|
netuid_i, amount_, current, staking_address_ss58, status=None
|
150
|
-
):
|
167
|
+
) -> bool:
|
151
168
|
err_out = partial(print_error, status=status)
|
152
|
-
current_balance = await
|
169
|
+
current_balance, next_nonce, call = await asyncio.gather(
|
170
|
+
subtensor.get_balance(wallet.coldkeypub.ss58_address),
|
171
|
+
subtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
|
172
|
+
subtensor.substrate.compose_call(
|
173
|
+
call_module="SubtensorModule",
|
174
|
+
call_function="add_stake",
|
175
|
+
call_params={
|
176
|
+
"hotkey": staking_address_ss58,
|
177
|
+
"netuid": netuid_i,
|
178
|
+
"amount_staked": amount_.rao,
|
179
|
+
},
|
180
|
+
),
|
181
|
+
)
|
153
182
|
failure_prelude = (
|
154
183
|
f":cross_mark: [red]Failed[/red] to stake {amount} on Netuid {netuid_i}"
|
155
184
|
)
|
156
|
-
next_nonce = await subtensor.substrate.get_account_next_index(
|
157
|
-
wallet.coldkeypub.ss58_address
|
158
|
-
)
|
159
|
-
call = await subtensor.substrate.compose_call(
|
160
|
-
call_module="SubtensorModule",
|
161
|
-
call_function="add_stake",
|
162
|
-
call_params={
|
163
|
-
"hotkey": staking_address_ss58,
|
164
|
-
"netuid": netuid_i,
|
165
|
-
"amount_staked": amount_.rao,
|
166
|
-
},
|
167
|
-
)
|
168
185
|
extrinsic = await subtensor.substrate.create_signed_extrinsic(
|
169
|
-
call=call, keypair=wallet.coldkey, nonce=next_nonce
|
186
|
+
call=call, keypair=wallet.coldkey, nonce=next_nonce, era={"period": era}
|
170
187
|
)
|
171
188
|
try:
|
172
189
|
response = await subtensor.substrate.submit_extrinsic(
|
@@ -174,35 +191,46 @@ async def stake_add(
|
|
174
191
|
)
|
175
192
|
except SubstrateRequestException as e:
|
176
193
|
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
|
177
|
-
return
|
194
|
+
return False
|
178
195
|
else:
|
179
|
-
await response.process_events()
|
180
196
|
if not await response.is_success:
|
181
197
|
err_out(
|
182
198
|
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
|
183
199
|
)
|
200
|
+
return False
|
184
201
|
else:
|
202
|
+
if json_output:
|
203
|
+
# the rest of this is not necessary if using json_output
|
204
|
+
return True
|
205
|
+
new_block_hash = await subtensor.substrate.get_chain_head()
|
185
206
|
new_balance, new_stake = await asyncio.gather(
|
186
|
-
subtensor.get_balance(
|
207
|
+
subtensor.get_balance(
|
208
|
+
wallet.coldkeypub.ss58_address, block_hash=new_block_hash
|
209
|
+
),
|
187
210
|
subtensor.get_stake(
|
188
211
|
hotkey_ss58=staking_address_ss58,
|
189
212
|
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
190
213
|
netuid=netuid_i,
|
214
|
+
block_hash=new_block_hash,
|
191
215
|
),
|
192
216
|
)
|
193
217
|
console.print(
|
194
|
-
f":white_heavy_check_mark:
|
218
|
+
f":white_heavy_check_mark: "
|
219
|
+
f"[dark_sea_green3]Finalized. Stake added to netuid: {netuid_i}[/dark_sea_green3]"
|
195
220
|
)
|
196
221
|
console.print(
|
197
|
-
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right:
|
222
|
+
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
|
223
|
+
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
|
198
224
|
)
|
199
225
|
console.print(
|
200
|
-
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]
|
226
|
+
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
|
227
|
+
f"{netuid_i}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
|
201
228
|
f"Stake:\n"
|
202
229
|
f" [blue]{current}[/blue] "
|
203
230
|
f":arrow_right: "
|
204
231
|
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
|
205
232
|
)
|
233
|
+
return True
|
206
234
|
|
207
235
|
netuids = (
|
208
236
|
[int(netuid)]
|
@@ -282,10 +310,24 @@ async def stake_add(
|
|
282
310
|
return False
|
283
311
|
remaining_wallet_balance -= amount_to_stake
|
284
312
|
|
285
|
-
|
286
|
-
|
287
|
-
|
313
|
+
stake_fee = await subtensor.get_stake_fee(
|
314
|
+
origin_hotkey_ss58=None,
|
315
|
+
origin_netuid=None,
|
316
|
+
origin_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
317
|
+
destination_hotkey_ss58=hotkey[1],
|
318
|
+
destination_netuid=netuid,
|
319
|
+
destination_coldkey_ss58=wallet.coldkeypub.ss58_address,
|
320
|
+
amount=amount_to_stake.rao,
|
288
321
|
)
|
322
|
+
|
323
|
+
# Calculate slippage
|
324
|
+
try:
|
325
|
+
received_amount, slippage_pct, slippage_pct_float, rate = (
|
326
|
+
_calculate_slippage(subnet_info, amount_to_stake, stake_fee)
|
327
|
+
)
|
328
|
+
except ValueError:
|
329
|
+
return False
|
330
|
+
|
289
331
|
max_slippage = max(slippage_pct_float, max_slippage)
|
290
332
|
|
291
333
|
# Add rows for the table
|
@@ -296,6 +338,7 @@ async def stake_add(
|
|
296
338
|
str(rate)
|
297
339
|
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
|
298
340
|
str(received_amount.set_unit(netuid)), # received
|
341
|
+
str(stake_fee), # fee
|
299
342
|
str(slippage_pct), # slippage
|
300
343
|
]
|
301
344
|
|
@@ -318,7 +361,9 @@ async def stake_add(
|
|
318
361
|
base_row.extend(
|
319
362
|
[
|
320
363
|
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
|
321
|
-
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]
|
364
|
+
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
|
365
|
+
# safe staking
|
366
|
+
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
|
322
367
|
]
|
323
368
|
)
|
324
369
|
|
@@ -337,7 +382,7 @@ async def stake_add(
|
|
337
382
|
return False
|
338
383
|
|
339
384
|
if safe_staking:
|
340
|
-
stake_coroutines =
|
385
|
+
stake_coroutines = {}
|
341
386
|
for i, (ni, am, curr, price_with_tolerance) in enumerate(
|
342
387
|
zip(
|
343
388
|
netuids, amounts_to_stake, current_stake_balances, prices_with_tolerance
|
@@ -346,27 +391,23 @@ async def stake_add(
|
|
346
391
|
for _, staking_address in hotkeys_to_stake_to:
|
347
392
|
# Regular extrinsic for root subnet
|
348
393
|
if ni == 0:
|
349
|
-
stake_coroutines
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
staking_address_ss58=staking_address,
|
355
|
-
)
|
394
|
+
stake_coroutines[(ni, staking_address)] = stake_extrinsic(
|
395
|
+
netuid_i=ni,
|
396
|
+
amount_=am,
|
397
|
+
current=curr,
|
398
|
+
staking_address_ss58=staking_address,
|
356
399
|
)
|
357
400
|
else:
|
358
|
-
stake_coroutines
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
price_limit=price_with_tolerance,
|
365
|
-
)
|
401
|
+
stake_coroutines[(ni, staking_address)] = safe_stake_extrinsic(
|
402
|
+
netuid_=ni,
|
403
|
+
amount_=am,
|
404
|
+
current_stake=curr,
|
405
|
+
hotkey_ss58_=staking_address,
|
406
|
+
price_limit=price_with_tolerance,
|
366
407
|
)
|
367
408
|
else:
|
368
|
-
stake_coroutines =
|
369
|
-
stake_extrinsic(
|
409
|
+
stake_coroutines = {
|
410
|
+
(ni, staking_address): stake_extrinsic(
|
370
411
|
netuid_i=ni,
|
371
412
|
amount_=am,
|
372
413
|
current=curr,
|
@@ -376,12 +417,15 @@ async def stake_add(
|
|
376
417
|
zip(netuids, amounts_to_stake, current_stake_balances)
|
377
418
|
)
|
378
419
|
for _, staking_address in hotkeys_to_stake_to
|
379
|
-
|
380
|
-
|
420
|
+
}
|
421
|
+
successes = defaultdict(dict)
|
381
422
|
with console.status(f"\n:satellite: Staking on netuid(s): {netuids} ..."):
|
382
423
|
# We can gather them all at once but balance reporting will be in race-condition.
|
383
|
-
for coroutine in stake_coroutines:
|
384
|
-
await coroutine
|
424
|
+
for (ni, staking_address), coroutine in stake_coroutines.items():
|
425
|
+
success = await coroutine
|
426
|
+
successes[ni][staking_address] = success
|
427
|
+
if json_output:
|
428
|
+
json_console.print(json.dumps({"staking_success": successes}))
|
385
429
|
|
386
430
|
|
387
431
|
# Helper functions
|
@@ -531,6 +575,11 @@ def _define_stake_table(
|
|
531
575
|
justify="center",
|
532
576
|
style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"],
|
533
577
|
)
|
578
|
+
table.add_column(
|
579
|
+
"Fee (τ)",
|
580
|
+
justify="center",
|
581
|
+
style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"],
|
582
|
+
)
|
534
583
|
table.add_column(
|
535
584
|
"Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"]
|
536
585
|
)
|
@@ -585,29 +634,41 @@ The columns are as follows:
|
|
585
634
|
|
586
635
|
|
587
636
|
def _calculate_slippage(
|
588
|
-
subnet_info, amount: Balance
|
637
|
+
subnet_info, amount: Balance, stake_fee: Balance
|
589
638
|
) -> tuple[Balance, str, float, str]:
|
590
639
|
"""Calculate slippage when adding stake.
|
591
640
|
|
592
641
|
Args:
|
593
642
|
subnet_info: Subnet dynamic info
|
594
643
|
amount: Amount being staked
|
644
|
+
stake_fee: Transaction fee for the stake operation
|
595
645
|
|
596
646
|
Returns:
|
597
647
|
tuple containing:
|
598
|
-
- received_amount: Amount received after slippage
|
648
|
+
- received_amount: Amount received after slippage and fees
|
599
649
|
- slippage_str: Formatted slippage percentage string
|
600
650
|
- slippage_float: Raw slippage percentage value
|
651
|
+
- rate: Exchange rate string
|
601
652
|
"""
|
602
|
-
|
603
|
-
|
604
|
-
|
653
|
+
amount_after_fee = amount - stake_fee
|
654
|
+
|
655
|
+
if amount_after_fee < 0:
|
656
|
+
print_error("You don't have enough balance to cover the stake fee.")
|
657
|
+
raise ValueError()
|
658
|
+
|
659
|
+
received_amount, _, _ = subnet_info.tao_to_alpha_with_slippage(amount_after_fee)
|
660
|
+
|
605
661
|
if subnet_info.is_dynamic:
|
662
|
+
ideal_amount = subnet_info.tao_to_alpha(amount)
|
663
|
+
total_slippage = ideal_amount - received_amount
|
664
|
+
slippage_pct_float = 100 * (total_slippage.tao / ideal_amount.tao)
|
606
665
|
slippage_str = f"{slippage_pct_float:.4f} %"
|
607
666
|
rate = f"{(1 / subnet_info.price.tao or 1):.4f}"
|
608
667
|
else:
|
609
|
-
slippage_pct_float =
|
610
|
-
|
668
|
+
slippage_pct_float = (
|
669
|
+
100 * float(stake_fee.tao) / float(amount.tao) if amount.tao != 0 else 0
|
670
|
+
)
|
671
|
+
slippage_str = f"{slippage_pct_float:.4f} %"
|
611
672
|
rate = "1"
|
612
673
|
|
613
674
|
return received_amount, slippage_str, slippage_pct_float, rate
|