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
):
42
46
"""
43
47
Args:
@@ -55,11 +59,13 @@ async def stake_add(
55
59
safe_staking: whether to use safe staking
56
60
rate_tolerance: rate tolerance percentage for stake operations
57
61
allow_partial_stake: whether to allow partial stake
62
+ json_output: whether to output stake info in JSON format
58
63
59
64
Returns:
60
65
bool: True if stake operation is successful, False otherwise
61
66
"""
62
67
68
+ # TODO name shadowing
63
69
async def safe_stake_extrinsic (
64
70
netuid : int ,
65
71
amount : Balance ,
@@ -69,25 +75,25 @@ async def safe_stake_extrinsic(
69
75
wallet : Wallet ,
70
76
subtensor : "SubtensorInterface" ,
71
77
status = None ,
72
- ) -> None :
78
+ ) -> bool :
73
79
err_out = partial (print_error , status = status )
74
80
failure_prelude = (
75
81
f":cross_mark: [red]Failed[/red] to stake { amount } on Netuid { netuid } "
76
82
)
77
- current_balance = await subtensor . get_balance ( wallet . coldkeypub . ss58_address )
78
- next_nonce = await subtensor .substrate . get_account_next_index (
79
- wallet .coldkeypub .ss58_address
80
- )
81
- call = await subtensor . substrate . compose_call (
82
- call_module = "SubtensorModule " ,
83
- call_function = "add_stake_limit" ,
84
- call_params = {
85
- "hotkey " : hotkey_ss58 ,
86
- "netuid " : netuid ,
87
- "amount_staked " : amount . rao ,
88
- "limit_price " : price_limit ,
89
- "allow_partial" : allow_partial_stake ,
90
- } ,
83
+ current_balance , next_nonce , call = await asyncio . gather (
84
+ subtensor .get_balance ( wallet . coldkeypub . ss58_address ),
85
+ subtensor . substrate . get_account_next_index ( wallet .coldkeypub .ss58_address ),
86
+ subtensor . substrate . compose_call (
87
+ call_module = "SubtensorModule" ,
88
+ call_function = "add_stake_limit " ,
89
+ call_params = {
90
+ "hotkey" : hotkey_ss58 ,
91
+ "netuid " : netuid ,
92
+ "amount_staked " : amount . rao ,
93
+ "limit_price " : price_limit ,
94
+ "allow_partial " : allow_partial_stake ,
95
+ } ,
96
+ ) ,
91
97
)
92
98
extrinsic = await subtensor .substrate .create_signed_extrinsic (
93
99
call = call , keypair = wallet .coldkey , nonce = next_nonce
@@ -104,71 +110,78 @@ async def safe_stake_extrinsic(
104
110
f"Either increase price tolerance or enable partial staking." ,
105
111
status = status ,
106
112
)
107
- return
113
+ return False
108
114
else :
109
115
err_out (f"\n { failure_prelude } with error: { format_error_message (e )} " )
110
- 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
111
122
else :
112
- await response .process_events ()
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
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 )]
@@ -322,7 +346,9 @@ async def stake_extrinsic(
322
346
base_row .extend (
323
347
[
324
348
f"{ rate_with_tolerance } { Balance .get_unit (netuid )} /{ Balance .get_unit (0 )} " ,
325
- f"[{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]{ allow_partial_stake } [/{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]" , # safe staking
349
+ f"[{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]"
350
+ # safe staking
351
+ f"{ allow_partial_stake } [/{ 'dark_sea_green3' if allow_partial_stake else 'red' } ]" ,
326
352
]
327
353
)
328
354
@@ -341,7 +367,7 @@ async def stake_extrinsic(
341
367
return False
342
368
343
369
if safe_staking :
344
- stake_coroutines = []
370
+ stake_coroutines = {}
345
371
for i , (ni , am , curr , price_with_tolerance ) in enumerate (
346
372
zip (
347
373
netuids , amounts_to_stake , current_stake_balances , prices_with_tolerance
@@ -350,29 +376,25 @@ async def stake_extrinsic(
350
376
for _ , staking_address in hotkeys_to_stake_to :
351
377
# Regular extrinsic for root subnet
352
378
if ni == 0 :
353
- stake_coroutines .append (
354
- stake_extrinsic (
355
- netuid_i = ni ,
356
- amount_ = am ,
357
- current = curr ,
358
- staking_address_ss58 = staking_address ,
359
- )
379
+ stake_coroutines [(ni , staking_address )] = stake_extrinsic (
380
+ netuid_i = ni ,
381
+ amount_ = am ,
382
+ current = curr ,
383
+ staking_address_ss58 = staking_address ,
360
384
)
361
385
else :
362
- stake_coroutines .append (
363
- safe_stake_extrinsic (
364
- netuid = ni ,
365
- amount = am ,
366
- current_stake = curr ,
367
- hotkey_ss58 = staking_address ,
368
- price_limit = price_with_tolerance ,
369
- wallet = wallet ,
370
- subtensor = subtensor ,
371
- )
386
+ stake_coroutines [(ni , staking_address )] = safe_stake_extrinsic (
387
+ netuid = ni ,
388
+ amount = am ,
389
+ current_stake = curr ,
390
+ hotkey_ss58 = staking_address ,
391
+ price_limit = price_with_tolerance ,
392
+ wallet = wallet ,
393
+ subtensor = subtensor ,
372
394
)
373
395
else :
374
- stake_coroutines = [
375
- stake_extrinsic (
396
+ stake_coroutines = {
397
+ ( ni , staking_address ): stake_extrinsic (
376
398
netuid_i = ni ,
377
399
amount_ = am ,
378
400
current = curr ,
@@ -382,12 +404,15 @@ async def stake_extrinsic(
382
404
zip (netuids , amounts_to_stake , current_stake_balances )
383
405
)
384
406
for _ , staking_address in hotkeys_to_stake_to
385
- ]
386
-
407
+ }
408
+ successes = defaultdict ( dict )
387
409
with console .status (f"\n :satellite: Staking on netuid(s): { netuids } ..." ):
388
410
# We can gather them all at once but balance reporting will be in race-condition.
389
- for coroutine in stake_coroutines :
390
- await coroutine
411
+ for (ni , staking_address ), coroutine in stake_coroutines .items ():
412
+ success = await coroutine
413
+ successes [ni ][staking_address ] = success
414
+ if json_output :
415
+ json_console .print (json .dumps ({"staking_success" : successes }))
391
416
392
417
393
418
# Helper functions
@@ -590,7 +615,9 @@ def _print_table_and_slippage(table: Table, max_slippage: float, safe_staking: b
590
615
console .print (base_description + (safe_staking_description if safe_staking else "" ))
591
616
592
617
593
- def _calculate_slippage (subnet_info , amount : Balance ) -> tuple [Balance , str , float ]:
618
+ def _calculate_slippage (
619
+ subnet_info , amount : Balance
620
+ ) -> tuple [Balance , str , float , str ]:
594
621
"""Calculate slippage when adding stake.
595
622
596
623
Args:
0 commit comments