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
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,7 @@ async def stake_add(
38
41
safe_staking : bool ,
39
42
rate_tolerance : float ,
40
43
allow_partial_stake : bool ,
44
+ json_output : bool ,
41
45
era : int ,
42
46
):
43
47
"""
@@ -54,6 +58,7 @@ async def stake_add(
54
58
safe_staking: whether to use safe staking
55
59
rate_tolerance: rate tolerance percentage for stake operations
56
60
allow_partial_stake: whether to allow partial stake
61
+ json_output: whether to output stake info in JSON format
57
62
era: Blocks for which the transaction should be valid.
58
63
59
64
Returns:
@@ -67,25 +72,25 @@ async def safe_stake_extrinsic(
67
72
hotkey_ss58_ : str ,
68
73
price_limit : Balance ,
69
74
status = None ,
70
- ) -> None :
75
+ ) -> bool :
71
76
err_out = partial (print_error , status = status )
72
77
failure_prelude = (
73
78
f":cross_mark: [red]Failed[/red] to stake { amount_ } on Netuid { netuid_ } "
74
79
)
75
- current_balance = await subtensor . get_balance ( wallet . coldkeypub . ss58_address )
76
- next_nonce = await subtensor .substrate . get_account_next_index (
77
- wallet .coldkeypub .ss58_address
78
- )
79
- call = await subtensor . substrate . compose_call (
80
- call_module = "SubtensorModule " ,
81
- call_function = "add_stake_limit" ,
82
- call_params = {
83
- "hotkey " : hotkey_ss58_ ,
84
- "netuid " : netuid_ ,
85
- "amount_staked " : amount_ . rao ,
86
- "limit_price " : price_limit ,
87
- "allow_partial" : allow_partial_stake ,
88
- } ,
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
+ ) ,
89
94
)
90
95
extrinsic = await subtensor .substrate .create_signed_extrinsic (
91
96
call = call ,
@@ -105,70 +110,78 @@ async def safe_stake_extrinsic(
105
110
f"Either increase price tolerance or enable partial staking." ,
106
111
status = status ,
107
112
)
108
- return
113
+ return False
109
114
else :
110
115
err_out (f"\n { failure_prelude } with error: { format_error_message (e )} " )
111
- 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
112
122
else :
113
- if not await response .is_success :
114
- err_out (
115
- f"\n { failure_prelude } with error: { format_error_message (await response .error_message )} "
116
- )
117
- else :
118
- block_hash = await subtensor .substrate .get_chain_head ()
119
- new_balance , new_stake = await asyncio .gather (
120
- subtensor .get_balance (wallet .coldkeypub .ss58_address , block_hash ),
121
- subtensor .get_stake (
122
- hotkey_ss58 = hotkey_ss58_ ,
123
- coldkey_ss58 = wallet .coldkeypub .ss58_address ,
124
- netuid = netuid_ ,
125
- block_hash = block_hash ,
126
- ),
127
- )
128
- console .print (
129
- f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: { netuid_ } [/dark_sea_green3]"
130
- )
131
- console .print (
132
- f"Balance:\n [blue]{ current_balance } [/blue] :arrow_right: [{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ]{ new_balance } "
133
- )
134
-
135
- amount_staked = current_balance - new_balance
136
- if allow_partial_stake and (amount_staked != amount_ ):
137
- console .print (
138
- "Partial stake transaction. Staked:\n "
139
- f" [{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ]{ amount_staked } [/{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ] "
140
- f"instead of "
141
- f"[blue]{ amount_ } [/blue]"
142
- )
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
+ )
143
144
145
+ amount_staked = current_balance - new_balance
146
+ if allow_partial_stake and (amount_staked != amount_ ):
144
147
console .print (
145
- f"Subnet: [ { COLOR_PALETTE [ 'GENERAL' ][ 'SUBHEADING' ] } ] { netuid_ } [/ { COLOR_PALETTE [ 'GENERAL' ][ 'SUBHEADING' ] } ] "
146
- f"Stake: \n "
147
- f" [blue] { current_stake } [/blue ] "
148
- f":arrow_right: "
149
- f"[{ COLOR_PALETTE [ 'STAKE' ][ 'STAKE_AMOUNT' ] } ] { new_stake } \n "
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] "
150
153
)
151
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
+
152
165
async def stake_extrinsic (
153
166
netuid_i , amount_ , current , staking_address_ss58 , status = None
154
- ):
167
+ ) -> bool :
155
168
err_out = partial (print_error , status = status )
156
- current_balance = await subtensor .get_balance (wallet .coldkeypub .ss58_address )
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
+ )
157
182
failure_prelude = (
158
183
f":cross_mark: [red]Failed[/red] to stake { amount } on Netuid { netuid_i } "
159
184
)
160
- next_nonce = await subtensor .substrate .get_account_next_index (
161
- wallet .coldkeypub .ss58_address
162
- )
163
- call = await subtensor .substrate .compose_call (
164
- call_module = "SubtensorModule" ,
165
- call_function = "add_stake" ,
166
- call_params = {
167
- "hotkey" : staking_address_ss58 ,
168
- "netuid" : netuid_i ,
169
- "amount_staked" : amount_ .rao ,
170
- },
171
- )
172
185
extrinsic = await subtensor .substrate .create_signed_extrinsic (
173
186
call = call , keypair = wallet .coldkey , nonce = next_nonce , era = {"period" : era }
174
187
)
@@ -178,35 +191,46 @@ async def stake_extrinsic(
178
191
)
179
192
except SubstrateRequestException as e :
180
193
err_out (f"\n { failure_prelude } with error: { format_error_message (e )} " )
181
- return
194
+ return False
182
195
else :
183
- await response .process_events ()
184
196
if not await response .is_success :
185
197
err_out (
186
198
f"\n { failure_prelude } with error: { format_error_message (await response .error_message )} "
187
199
)
200
+ return False
188
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 ()
189
206
new_balance , new_stake = await asyncio .gather (
190
- subtensor .get_balance (wallet .coldkeypub .ss58_address ),
207
+ subtensor .get_balance (
208
+ wallet .coldkeypub .ss58_address , block_hash = new_block_hash
209
+ ),
191
210
subtensor .get_stake (
192
211
hotkey_ss58 = staking_address_ss58 ,
193
212
coldkey_ss58 = wallet .coldkeypub .ss58_address ,
194
213
netuid = netuid_i ,
214
+ block_hash = new_block_hash ,
195
215
),
196
216
)
197
217
console .print (
198
- f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: { netuid_i } [/dark_sea_green3]"
218
+ f":white_heavy_check_mark: "
219
+ f"[dark_sea_green3]Finalized. Stake added to netuid: { netuid_i } [/dark_sea_green3]"
199
220
)
200
221
console .print (
201
- f"Balance:\n [blue]{ current_balance } [/blue] :arrow_right: [{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ]{ new_balance } "
222
+ f"Balance:\n [blue]{ current_balance } [/blue] :arrow_right: "
223
+ f"[{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ]{ new_balance } "
202
224
)
203
225
console .print (
204
- f"Subnet: [{ COLOR_PALETTE ['GENERAL' ]['SUBHEADING' ]} ]{ netuid_i } [/{ COLOR_PALETTE ['GENERAL' ]['SUBHEADING' ]} ] "
226
+ f"Subnet: [{ COLOR_PALETTE ['GENERAL' ]['SUBHEADING' ]} ]"
227
+ f"{ netuid_i } [/{ COLOR_PALETTE ['GENERAL' ]['SUBHEADING' ]} ] "
205
228
f"Stake:\n "
206
229
f" [blue]{ current } [/blue] "
207
230
f":arrow_right: "
208
231
f"[{ COLOR_PALETTE ['STAKE' ]['STAKE_AMOUNT' ]} ]{ new_stake } \n "
209
232
)
233
+ return True
210
234
211
235
netuids = (
212
236
[int (netuid )]
@@ -337,7 +361,9 @@ async def stake_extrinsic(
337
361
base_row .extend (
338
362
[
339
363
f"{ rate_with_tolerance } { Balance .get_unit (netuid )} /{ Balance .get_unit (0 )} " ,
340
- f"[{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]{ allow_partial_stake } [/{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]" , # safe staking
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' } ]" ,
341
367
]
342
368
)
343
369
@@ -356,7 +382,7 @@ async def stake_extrinsic(
356
382
return False
357
383
358
384
if safe_staking :
359
- stake_coroutines = []
385
+ stake_coroutines = {}
360
386
for i , (ni , am , curr , price_with_tolerance ) in enumerate (
361
387
zip (
362
388
netuids , amounts_to_stake , current_stake_balances , prices_with_tolerance
@@ -365,27 +391,23 @@ async def stake_extrinsic(
365
391
for _ , staking_address in hotkeys_to_stake_to :
366
392
# Regular extrinsic for root subnet
367
393
if ni == 0 :
368
- stake_coroutines .append (
369
- stake_extrinsic (
370
- netuid_i = ni ,
371
- amount_ = am ,
372
- current = curr ,
373
- staking_address_ss58 = staking_address ,
374
- )
394
+ stake_coroutines [(ni , staking_address )] = stake_extrinsic (
395
+ netuid_i = ni ,
396
+ amount_ = am ,
397
+ current = curr ,
398
+ staking_address_ss58 = staking_address ,
375
399
)
376
400
else :
377
- stake_coroutines .append (
378
- safe_stake_extrinsic (
379
- netuid_ = ni ,
380
- amount_ = am ,
381
- current_stake = curr ,
382
- hotkey_ss58_ = staking_address ,
383
- price_limit = price_with_tolerance ,
384
- )
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 ,
385
407
)
386
408
else :
387
- stake_coroutines = [
388
- stake_extrinsic (
409
+ stake_coroutines = {
410
+ ( ni , staking_address ): stake_extrinsic (
389
411
netuid_i = ni ,
390
412
amount_ = am ,
391
413
current = curr ,
@@ -395,12 +417,15 @@ async def stake_extrinsic(
395
417
zip (netuids , amounts_to_stake , current_stake_balances )
396
418
)
397
419
for _ , staking_address in hotkeys_to_stake_to
398
- ]
399
-
420
+ }
421
+ successes = defaultdict ( dict )
400
422
with console .status (f"\n :satellite: Staking on netuid(s): { netuids } ..." ):
401
423
# We can gather them all at once but balance reporting will be in race-condition.
402
- for coroutine in stake_coroutines :
403
- 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 }))
404
429
405
430
406
431
# Helper functions
0 commit comments