Skip to content

Commit bb28ab4

Browse files
authored
Merge pull request #369 from opentensor/feat/thewhaleking/json-output
json output for commands
2 parents 6ce3b86 + cc395cf commit bb28ab4

File tree

16 files changed

+1335
-454
lines changed

16 files changed

+1335
-454
lines changed

bittensor_cli/cli.py

Lines changed: 207 additions & 75 deletions
Large diffs are not rendered by default.

bittensor_cli/src/bittensor/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
3434

3535
console = Console()
36+
json_console = Console()
3637
err_console = Console(stderr=True)
3738
verbose_console = Console(quiet=True)
3839

bittensor_cli/src/commands/stake/add.py

Lines changed: 120 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import asyncio
2+
import json
3+
from collections import defaultdict
24
from functools import partial
35

46
from typing import TYPE_CHECKING, Optional
@@ -17,6 +19,7 @@
1719
print_error,
1820
print_verbose,
1921
unlock_key,
22+
json_console,
2023
)
2124
from bittensor_wallet import Wallet
2225

@@ -38,6 +41,7 @@ async def stake_add(
3841
safe_staking: bool,
3942
rate_tolerance: float,
4043
allow_partial_stake: bool,
44+
json_output: bool,
4145
era: int,
4246
):
4347
"""
@@ -54,6 +58,7 @@ async def stake_add(
5458
safe_staking: whether to use safe staking
5559
rate_tolerance: rate tolerance percentage for stake operations
5660
allow_partial_stake: whether to allow partial stake
61+
json_output: whether to output stake info in JSON format
5762
era: Blocks for which the transaction should be valid.
5863
5964
Returns:
@@ -67,25 +72,25 @@ async def safe_stake_extrinsic(
6772
hotkey_ss58_: str,
6873
price_limit: Balance,
6974
status=None,
70-
) -> None:
75+
) -> bool:
7176
err_out = partial(print_error, status=status)
7277
failure_prelude = (
7378
f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}"
7479
)
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+
),
8994
)
9095
extrinsic = await subtensor.substrate.create_signed_extrinsic(
9196
call=call,
@@ -105,70 +110,78 @@ async def safe_stake_extrinsic(
105110
f"Either increase price tolerance or enable partial staking.",
106111
status=status,
107112
)
108-
return
113+
return False
109114
else:
110115
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
112122
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+
)
143144

145+
amount_staked = current_balance - new_balance
146+
if allow_partial_stake and (amount_staked != amount_):
144147
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]"
150153
)
151154

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+
152165
async def stake_extrinsic(
153166
netuid_i, amount_, current, staking_address_ss58, status=None
154-
):
167+
) -> bool:
155168
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+
)
157182
failure_prelude = (
158183
f":cross_mark: [red]Failed[/red] to stake {amount} on Netuid {netuid_i}"
159184
)
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-
)
172185
extrinsic = await subtensor.substrate.create_signed_extrinsic(
173186
call=call, keypair=wallet.coldkey, nonce=next_nonce, era={"period": era}
174187
)
@@ -178,35 +191,46 @@ async def stake_extrinsic(
178191
)
179192
except SubstrateRequestException as e:
180193
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
181-
return
194+
return False
182195
else:
183-
await response.process_events()
184196
if not await response.is_success:
185197
err_out(
186198
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
187199
)
200+
return False
188201
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()
189206
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+
),
191210
subtensor.get_stake(
192211
hotkey_ss58=staking_address_ss58,
193212
coldkey_ss58=wallet.coldkeypub.ss58_address,
194213
netuid=netuid_i,
214+
block_hash=new_block_hash,
195215
),
196216
)
197217
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]"
199220
)
200221
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}"
202224
)
203225
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']}] "
205228
f"Stake:\n"
206229
f" [blue]{current}[/blue] "
207230
f":arrow_right: "
208231
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
209232
)
233+
return True
210234

211235
netuids = (
212236
[int(netuid)]
@@ -337,7 +361,9 @@ async def stake_extrinsic(
337361
base_row.extend(
338362
[
339363
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'}]",
341367
]
342368
)
343369

@@ -356,7 +382,7 @@ async def stake_extrinsic(
356382
return False
357383

358384
if safe_staking:
359-
stake_coroutines = []
385+
stake_coroutines = {}
360386
for i, (ni, am, curr, price_with_tolerance) in enumerate(
361387
zip(
362388
netuids, amounts_to_stake, current_stake_balances, prices_with_tolerance
@@ -365,27 +391,23 @@ async def stake_extrinsic(
365391
for _, staking_address in hotkeys_to_stake_to:
366392
# Regular extrinsic for root subnet
367393
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,
375399
)
376400
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,
385407
)
386408
else:
387-
stake_coroutines = [
388-
stake_extrinsic(
409+
stake_coroutines = {
410+
(ni, staking_address): stake_extrinsic(
389411
netuid_i=ni,
390412
amount_=am,
391413
current=curr,
@@ -395,12 +417,15 @@ async def stake_extrinsic(
395417
zip(netuids, amounts_to_stake, current_stake_balances)
396418
)
397419
for _, staking_address in hotkeys_to_stake_to
398-
]
399-
420+
}
421+
successes = defaultdict(dict)
400422
with console.status(f"\n:satellite: Staking on netuid(s): {netuids} ..."):
401423
# 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}))
404429

405430

406431
# Helper functions

0 commit comments

Comments
 (0)