Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
912f898
Init commit
thewhaleking Mar 7, 2025
1573a80
General improvements to wallet overview
thewhaleking Mar 7, 2025
ed7a43b
Wallet overview
thewhaleking Mar 7, 2025
ddf26e4
Var naming
thewhaleking Mar 7, 2025
cdf7ee4
mypy
thewhaleking Mar 7, 2025
3a752bf
Merge branch 'staging' into feat/thewhaleking/json-output
thewhaleking Mar 8, 2025
44a7482
Change json_output to use a separate console which allows us to quiet…
thewhaleking Mar 10, 2025
de55ab1
Merge remote-tracking branch 'origin/feat/thewhaleking/json-output' i…
thewhaleking Mar 10, 2025
28e8b41
More wallets commands
thewhaleking Mar 10, 2025
1fc534a
Wallets commands klaar
thewhaleking Mar 10, 2025
7dd4fe5
Stake List
thewhaleking Mar 10, 2025
d64edf2
stake add
thewhaleking Mar 11, 2025
3349cf6
stake remove
thewhaleking Mar 11, 2025
9b1fbce
More stake commands.
thewhaleking Mar 11, 2025
bdbf76b
Childkey commands (not all yet)
thewhaleking Mar 12, 2025
b4222f9
Merge remote-tracking branch 'origin/staging' into feat/thewhaleking/…
thewhaleking Mar 13, 2025
8f0437c
Merge conflict
thewhaleking Mar 13, 2025
1faddeb
Childkey take
thewhaleking Mar 13, 2025
419fa6b
Merge branch 'staging' into feat/thewhaleking/json-output
thewhaleking Mar 14, 2025
ad64a11
Sudo commands.
thewhaleking Mar 14, 2025
70138f3
Subnets list
thewhaleking Mar 14, 2025
c98d8ff
Subnets price
thewhaleking Mar 14, 2025
fb22b42
Subnets show check-in (incomplete)
thewhaleking Mar 14, 2025
87abec7
update to use price var
ibraheem-abe Mar 28, 2025
c2bbef9
Merge branch 'staging' into feat/thewhaleking/json-output
thewhaleking Apr 1, 2025
9703584
Ruff
thewhaleking Apr 1, 2025
3a92f61
Merge remote-tracking branch 'origin/feat/thewhaleking/json-output' i…
thewhaleking Apr 1, 2025
337c04e
temporary
thewhaleking Apr 1, 2025
512a295
Test senate
thewhaleking Apr 1, 2025
cb9d160
Test Staking
thewhaleking Apr 1, 2025
20f8c4a
Test Unstaking
thewhaleking Apr 1, 2025
bd81cba
test
thewhaleking Apr 2, 2025
b5cf8ae
Test Wallet Creations
thewhaleking Apr 2, 2025
8152b1e
Wallet Balances
thewhaleking Apr 2, 2025
2f1c60d
Updates test_wallet_creations.py
thewhaleking Apr 2, 2025
87cfda9
Bumps btwallet requirement.
thewhaleking Apr 2, 2025
cc395cf
Trigger no-op
thewhaleking Apr 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 207 additions & 75 deletions bittensor_cli/cli.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters

console = Console()
json_console = Console()
err_console = Console(stderr=True)
verbose_console = Console(quiet=True)

Expand Down
215 changes: 120 additions & 95 deletions bittensor_cli/src/commands/stake/add.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import asyncio
import json
from collections import defaultdict
from functools import partial

from typing import TYPE_CHECKING, Optional
Expand All @@ -17,6 +19,7 @@
print_error,
print_verbose,
unlock_key,
json_console,
)
from bittensor_wallet import Wallet

Expand All @@ -38,6 +41,7 @@ async def stake_add(
safe_staking: bool,
rate_tolerance: float,
allow_partial_stake: bool,
json_output: bool,
era: int,
):
"""
Expand All @@ -54,6 +58,7 @@ async def stake_add(
safe_staking: whether to use safe staking
rate_tolerance: rate tolerance percentage for stake operations
allow_partial_stake: whether to allow partial stake
json_output: whether to output stake info in JSON format
era: Blocks for which the transaction should be valid.

Returns:
Expand All @@ -67,25 +72,25 @@ async def safe_stake_extrinsic(
hotkey_ss58_: str,
price_limit: Balance,
status=None,
) -> None:
) -> bool:
err_out = partial(print_error, status=status)
failure_prelude = (
f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}"
)
current_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
next_nonce = await subtensor.substrate.get_account_next_index(
wallet.coldkeypub.ss58_address
)
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="add_stake_limit",
call_params={
"hotkey": hotkey_ss58_,
"netuid": netuid_,
"amount_staked": amount_.rao,
"limit_price": price_limit,
"allow_partial": allow_partial_stake,
},
current_balance, next_nonce, call = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address),
subtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="add_stake_limit",
call_params={
"hotkey": hotkey_ss58_,
"netuid": netuid_,
"amount_staked": amount_.rao,
"limit_price": price_limit,
"allow_partial": allow_partial_stake,
},
),
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call,
Expand All @@ -105,70 +110,78 @@ async def safe_stake_extrinsic(
f"Either increase price tolerance or enable partial staking.",
status=status,
)
return
return False
else:
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
return
return False
if not await response.is_success:
err_out(
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
)
return False
else:
if not await response.is_success:
err_out(
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
)
else:
block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
subtensor.get_stake(
hotkey_ss58=hotkey_ss58_,
coldkey_ss58=wallet.coldkeypub.ss58_address,
netuid=netuid_,
block_hash=block_hash,
),
)
console.print(
f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: {netuid_}[/dark_sea_green3]"
)
console.print(
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
)

amount_staked = current_balance - new_balance
if allow_partial_stake and (amount_staked != amount_):
console.print(
"Partial stake transaction. Staked:\n"
f" [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_staked}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] "
f"instead of "
f"[blue]{amount_}[/blue]"
)
if json_output:
# the rest of this checking is not necessary if using json_output
return True
block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
subtensor.get_stake(
hotkey_ss58=hotkey_ss58_,
coldkey_ss58=wallet.coldkeypub.ss58_address,
netuid=netuid_,
block_hash=block_hash,
),
)
console.print(
f":white_heavy_check_mark: [dark_sea_green3]Finalized. "
f"Stake added to netuid: {netuid_}[/dark_sea_green3]"
)
console.print(
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
)

amount_staked = current_balance - new_balance
if allow_partial_stake and (amount_staked != amount_):
console.print(
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid_}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
f"Stake:\n"
f" [blue]{current_stake}[/blue] "
f":arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
"Partial stake transaction. Staked:\n"
f" [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_staked}"
f"[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] "
f"instead of "
f"[blue]{amount_}[/blue]"
)

console.print(
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
f"{netuid_}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
f"Stake:\n"
f" [blue]{current_stake}[/blue] "
f":arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
)
return True

async def stake_extrinsic(
netuid_i, amount_, current, staking_address_ss58, status=None
):
) -> bool:
err_out = partial(print_error, status=status)
current_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
current_balance, next_nonce, call = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address),
subtensor.substrate.get_account_next_index(wallet.coldkeypub.ss58_address),
subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="add_stake",
call_params={
"hotkey": staking_address_ss58,
"netuid": netuid_i,
"amount_staked": amount_.rao,
},
),
)
failure_prelude = (
f":cross_mark: [red]Failed[/red] to stake {amount} on Netuid {netuid_i}"
)
next_nonce = await subtensor.substrate.get_account_next_index(
wallet.coldkeypub.ss58_address
)
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="add_stake",
call_params={
"hotkey": staking_address_ss58,
"netuid": netuid_i,
"amount_staked": amount_.rao,
},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.coldkey, nonce=next_nonce, era={"period": era}
)
Expand All @@ -178,35 +191,46 @@ async def stake_extrinsic(
)
except SubstrateRequestException as e:
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
return
return False
else:
await response.process_events()
if not await response.is_success:
err_out(
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
)
return False
else:
if json_output:
# the rest of this is not necessary if using json_output
return True
new_block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address),
subtensor.get_balance(
wallet.coldkeypub.ss58_address, block_hash=new_block_hash
),
subtensor.get_stake(
hotkey_ss58=staking_address_ss58,
coldkey_ss58=wallet.coldkeypub.ss58_address,
netuid=netuid_i,
block_hash=new_block_hash,
),
)
console.print(
f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: {netuid_i}[/dark_sea_green3]"
f":white_heavy_check_mark: "
f"[dark_sea_green3]Finalized. Stake added to netuid: {netuid_i}[/dark_sea_green3]"
)
console.print(
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
)
console.print(
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid_i}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
f"{netuid_i}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
f"Stake:\n"
f" [blue]{current}[/blue] "
f":arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
)
return True

netuids = (
[int(netuid)]
Expand Down Expand Up @@ -337,7 +361,9 @@ async def stake_extrinsic(
base_row.extend(
[
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]", # safe staking
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
# safe staking
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
]
)

Expand All @@ -356,7 +382,7 @@ async def stake_extrinsic(
return False

if safe_staking:
stake_coroutines = []
stake_coroutines = {}
for i, (ni, am, curr, price_with_tolerance) in enumerate(
zip(
netuids, amounts_to_stake, current_stake_balances, prices_with_tolerance
Expand All @@ -365,27 +391,23 @@ async def stake_extrinsic(
for _, staking_address in hotkeys_to_stake_to:
# Regular extrinsic for root subnet
if ni == 0:
stake_coroutines.append(
stake_extrinsic(
netuid_i=ni,
amount_=am,
current=curr,
staking_address_ss58=staking_address,
)
stake_coroutines[(ni, staking_address)] = stake_extrinsic(
netuid_i=ni,
amount_=am,
current=curr,
staking_address_ss58=staking_address,
)
else:
stake_coroutines.append(
safe_stake_extrinsic(
netuid_=ni,
amount_=am,
current_stake=curr,
hotkey_ss58_=staking_address,
price_limit=price_with_tolerance,
)
stake_coroutines[(ni, staking_address)] = safe_stake_extrinsic(
netuid_=ni,
amount_=am,
current_stake=curr,
hotkey_ss58_=staking_address,
price_limit=price_with_tolerance,
)
else:
stake_coroutines = [
stake_extrinsic(
stake_coroutines = {
(ni, staking_address): stake_extrinsic(
netuid_i=ni,
amount_=am,
current=curr,
Expand All @@ -395,12 +417,15 @@ async def stake_extrinsic(
zip(netuids, amounts_to_stake, current_stake_balances)
)
for _, staking_address in hotkeys_to_stake_to
]

}
successes = defaultdict(dict)
with console.status(f"\n:satellite: Staking on netuid(s): {netuids} ..."):
# We can gather them all at once but balance reporting will be in race-condition.
for coroutine in stake_coroutines:
await coroutine
for (ni, staking_address), coroutine in stake_coroutines.items():
success = await coroutine
successes[ni][staking_address] = success
if json_output:
json_console.print(json.dumps({"staking_success": successes}))


# Helper functions
Expand Down
Loading
Loading