diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py
index 4484f5288..20ddb90d1 100755
--- a/bittensor_cli/cli.py
+++ b/bittensor_cli/cli.py
@@ -4379,7 +4379,7 @@ def stake_move(
f"interactive_selection: {interactive_selection}\n"
f"prompt: {prompt}\n"
)
- result = self._run_command(
+ result, ext_id = self._run_command(
move_stake.move_stake(
subtensor=self.initialize_chain(network),
wallet=wallet,
@@ -4395,7 +4395,9 @@ def stake_move(
)
)
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id or None})
+ )
return result
def stake_transfer(
@@ -4555,7 +4557,7 @@ def stake_transfer(
f"era: {period}\n"
f"stake_all: {stake_all}"
)
- result = self._run_command(
+ result, ext_id = self._run_command(
move_stake.transfer_stake(
wallet=wallet,
subtensor=self.initialize_chain(network),
@@ -4571,7 +4573,9 @@ def stake_transfer(
)
)
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id or None})
+ )
return result
def stake_swap(
@@ -4675,7 +4679,7 @@ def stake_swap(
f"wait_for_inclusion: {wait_for_inclusion}\n"
f"wait_for_finalization: {wait_for_finalization}\n"
)
- result = self._run_command(
+ result, ext_id = self._run_command(
move_stake.swap_stake(
wallet=wallet,
subtensor=self.initialize_chain(network),
@@ -4691,7 +4695,9 @@ def stake_swap(
)
)
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id or None})
+ )
return result
def stake_get_children(
@@ -4990,7 +4996,7 @@ def stake_childkey_take(
f"wait_for_inclusion: {wait_for_inclusion}\n"
f"wait_for_finalization: {wait_for_finalization}\n"
)
- results: list[tuple[Optional[int], bool]] = self._run_command(
+ results: list[tuple[Optional[int], bool, Optional[str]]] = self._run_command(
children_hotkeys.childkey_take(
wallet=wallet,
subtensor=self.initialize_chain(network),
@@ -5004,8 +5010,8 @@ def stake_childkey_take(
)
if json_output:
output = {}
- for netuid_, success in results:
- output[netuid_] = success
+ for netuid_, success, ext_id in results:
+ output[netuid_] = {"success": success, "extrinsic_identifier": ext_id}
json_console.print(json.dumps(output))
return results
@@ -5128,7 +5134,7 @@ def sudo_set(
f"param_name: {param_name}\n"
f"param_value: {param_value}"
)
- result, err_msg = self._run_command(
+ result, err_msg, ext_id = self._run_command(
sudo.sudo_set_hyperparameter(
wallet,
self.initialize_chain(network),
@@ -5140,7 +5146,15 @@ def sudo_set(
)
)
if json_output:
- json_console.print(json.dumps({"success": result, "err_msg": err_msg}))
+ json_console.print(
+ json.dumps(
+ {
+ "success": result,
+ "err_msg": err_msg,
+ "extrinsic_identifier": ext_id,
+ }
+ )
+ )
return result
def sudo_get(
@@ -5225,7 +5239,7 @@ def sudo_senate_vote(
None,
"--vote-aye/--vote-nay",
prompt="Enter y to vote Aye, or enter n to vote Nay",
- help="The vote casted on the proposal",
+ help="The vote cast on the proposal",
),
):
"""
@@ -5302,11 +5316,13 @@ def sudo_set_take(
)
raise typer.Exit()
logger.debug(f"args:\nnetwork: {network}\ntake: {take}")
- result = self._run_command(
+ result, ext_id = self._run_command(
sudo.set_take(wallet, self.initialize_chain(network), take)
)
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id})
+ )
return result
def sudo_get_take(
@@ -5870,13 +5886,15 @@ def subnets_set_identity(
logger.debug(
f"args:\nnetwork: {network}\nnetuid: {netuid}\nidentity: {identity}"
)
- success = self._run_command(
+ success, ext_id = self._run_command(
subnets.set_identity(
wallet, self.initialize_chain(network), netuid, identity, prompt
)
)
if json_output:
- json_console.print(json.dumps({"success": success}))
+ json_console.print(
+ json.dumps({"success": success, "extrinsic_identifier": ext_id})
+ )
def subnets_pow_register(
self,
@@ -6204,6 +6222,7 @@ def weights_reveal(
[green]$[/green] btcli wt reveal --netuid 1 --uids 1,2,3,4 --weights 0.1,0.2,0.3,0.4 --salt 163,241,217,11,161,142,147,189
"""
self.verbosity_handler(quiet, verbose, json_output)
+ # TODO think we need to ','.split uids and weights ?
uids = list_prompt(uids, int, "UIDs of interest for the specified netuid")
weights = list_prompt(
weights, float, "Corresponding weights for the specified UIDs"
diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py
index 2c2371761..a32bc1c3d 100644
--- a/bittensor_cli/src/bittensor/extrinsics/registration.py
+++ b/bittensor_cli/src/bittensor/extrinsics/registration.py
@@ -18,6 +18,7 @@
from typing import Optional
import subprocess
+from async_substrate_interface import AsyncExtrinsicReceipt
from bittensor_wallet import Wallet
from Crypto.Hash import keccak
import numpy as np
@@ -40,6 +41,7 @@
unlock_key,
hex_to_bytes,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if typing.TYPE_CHECKING:
@@ -679,7 +681,7 @@ async def burned_register_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
era: Optional[int] = None,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
"""Registers the wallet to chain by recycling TAO.
:param subtensor: The SubtensorInterface object to use for the call, initialized
@@ -698,7 +700,7 @@ async def burned_register_extrinsic(
"""
if not (unlock_status := unlock_key(wallet, print_out=False)).success:
- return False, unlock_status.message
+ return False, unlock_status.message, None
with console.status(
f":satellite: Checking Account on [bold]subnet:{netuid}[/bold]...",
@@ -742,7 +744,7 @@ async def burned_register_extrinsic(
f"hotkey: [{COLOR_PALETTE.G.HK}]{neuron.hotkey}[/{COLOR_PALETTE.G.HK}]\n"
f"coldkey: [{COLOR_PALETTE.G.CK}]{neuron.coldkey}[/{COLOR_PALETTE.G.CK}]"
)
- return True, "Already registered"
+ return True, "Already registered", None
with console.status(
":satellite: Recycling TAO for Registration...", spinner="aesthetic"
@@ -755,16 +757,18 @@ async def burned_register_extrinsic(
"hotkey": get_hotkey_pub_ss58(wallet),
},
)
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization, era=era_
)
if not success:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
await asyncio.sleep(0.5)
- return False, err_msg
+ return False, err_msg, None
# Successful registration, final check for neuron and pubkey
else:
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
with console.status(":satellite: Checking Balance...", spinner="aesthetic"):
block_hash = await subtensor.substrate.get_chain_head()
new_balance, netuids_for_hotkey, my_uid = await asyncio.gather(
@@ -791,13 +795,13 @@ async def burned_register_extrinsic(
console.print(
f":white_heavy_check_mark: [green]Registered on netuid {netuid} with UID {my_uid}[/green]"
)
- return True, f"Registered on {netuid} with UID {my_uid}"
+ return True, f"Registered on {netuid} with UID {my_uid}", ext_id
else:
# neuron not found, try again
err_console.print(
":cross_mark: [red]Unknown error. Neuron not found.[/red]"
)
- return False, "Unknown error. Neuron not found."
+ return False, "Unknown error. Neuron not found.", ext_id
async def run_faucet_extrinsic(
@@ -1749,7 +1753,7 @@ async def swap_hotkey_extrinsic(
new_wallet: Wallet,
netuid: Optional[int] = None,
prompt: bool = False,
-) -> bool:
+) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""
Performs an extrinsic update for swapping two hotkeys on the chain
@@ -1770,14 +1774,14 @@ async def swap_hotkey_extrinsic(
err_console.print(
f":cross_mark: [red]Failed[/red]: Original hotkey {hk_ss58} is not registered on subnet {netuid}"
)
- return False
+ return False, None
elif not len(netuids_registered) > 0:
err_console.print(
f"Original hotkey [dark_orange]{hk_ss58}[/dark_orange] is not registered on any subnet. "
f"Please register and try again"
)
- return False
+ return False, None
if netuid is not None:
if netuid in netuids_registered_new_hotkey:
@@ -1785,17 +1789,17 @@ async def swap_hotkey_extrinsic(
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
f"is already registered on subnet {netuid}"
)
- return False
+ return False, None
else:
if len(netuids_registered_new_hotkey) > 0:
err_console.print(
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
f"is already registered on subnet(s) {netuids_registered_new_hotkey}"
)
- return False
+ return False, None
if not unlock_key(wallet).success:
- return False
+ return False, None
if prompt:
# Prompt user for confirmation.
@@ -1815,7 +1819,7 @@ async def swap_hotkey_extrinsic(
)
if not Confirm.ask(confirm_message):
- return False
+ return False, None
print_verbose(
f"Swapping {wallet.name}'s hotkey ({hk_ss58} - {wallet.hotkey_str}) with "
f"{new_wallet.name}'s hotkey ({new_hk_ss58} - {new_wallet.hotkey_str})"
@@ -1832,15 +1836,17 @@ async def swap_hotkey_extrinsic(
call_function="swap_hotkey",
call_params=call_params,
)
- success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
+ call, wallet
+ )
if success:
console.print(
f"Hotkey {hk_ss58} ({wallet.hotkey_str}) swapped for new hotkey: "
f"{new_hk_ss58} ({new_wallet.hotkey_str})"
)
- return True
+ return True, ext_receipt
else:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
time.sleep(0.5)
- return False
+ return False, ext_receipt
diff --git a/bittensor_cli/src/bittensor/extrinsics/root.py b/bittensor_cli/src/bittensor/extrinsics/root.py
index 207fb8642..ea515ed1a 100644
--- a/bittensor_cli/src/bittensor/extrinsics/root.py
+++ b/bittensor_cli/src/bittensor/extrinsics/root.py
@@ -18,7 +18,7 @@
import asyncio
import hashlib
import time
-from typing import Union, List, TYPE_CHECKING
+from typing import Union, List, TYPE_CHECKING, Optional
from bittensor_wallet import Wallet, Keypair
import numpy as np
@@ -38,6 +38,7 @@
format_error_message,
unlock_key,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if TYPE_CHECKING:
@@ -291,7 +292,7 @@ async def root_register_extrinsic(
wallet: Wallet,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
r"""Registers the wallet to root network.
:param subtensor: The SubtensorInterface object
@@ -307,7 +308,7 @@ async def root_register_extrinsic(
"""
if not (unlock := unlock_key(wallet)).success:
- return False, unlock.message
+ return False, unlock.message, None
print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
is_registered = await is_hotkey_registered(
@@ -317,7 +318,7 @@ async def root_register_extrinsic(
console.print(
":white_heavy_check_mark: [green]Already registered on root network.[/green]"
)
- return True, "Already registered on root network"
+ return True, "Already registered on root network", None
with console.status(":satellite: Registering to root network...", spinner="earth"):
call = await subtensor.substrate.compose_call(
@@ -325,7 +326,7 @@ async def root_register_extrinsic(
call_function="root_register",
call_params={"hotkey": get_hotkey_pub_ss58(wallet)},
)
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call,
wallet=wallet,
wait_for_inclusion=wait_for_inclusion,
@@ -335,10 +336,12 @@ async def root_register_extrinsic(
if not success:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
await asyncio.sleep(0.5)
- return False, err_msg
+ return False, err_msg, None
# Successful registration, final check for neuron and pubkey
else:
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
uid = await subtensor.query(
module="SubtensorModule",
storage_function="Uids",
@@ -348,13 +351,13 @@ async def root_register_extrinsic(
console.print(
f":white_heavy_check_mark: [green]Registered with UID {uid}[/green]"
)
- return True, f"Registered with UID {uid}"
+ return True, f"Registered with UID {uid}", ext_id
else:
# neuron not found, try again
err_console.print(
":cross_mark: [red]Unknown error. Neuron not found.[/red]"
)
- return False, "Unknown error. Neuron not found."
+ return False, "Unknown error. Neuron not found.", ext_id
async def set_root_weights_extrinsic(
diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py
index ad3168a23..65ecfbcf8 100644
--- a/bittensor_cli/src/bittensor/extrinsics/transfer.py
+++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py
@@ -1,10 +1,11 @@
import asyncio
+from typing import Optional, Union
+from async_substrate_interface import AsyncExtrinsicReceipt
from bittensor_wallet import Wallet
from rich.prompt import Confirm
from async_substrate_interface.errors import SubstrateRequestException
-from bittensor_cli.src import NETWORK_EXPLORER_MAP
from bittensor_cli.src.bittensor.balances import Balance
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
from bittensor_cli.src.bittensor.utils import (
@@ -12,7 +13,6 @@
err_console,
print_verbose,
format_error_message,
- get_explorer_url_for_network,
is_valid_bittensor_address_or_public_key,
print_error,
unlock_key,
@@ -30,7 +30,7 @@ async def transfer_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = False,
-) -> bool:
+) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""Transfers funds from this wallet to the destination public key address.
:param subtensor: initialized SubtensorInterface object used for transfer
@@ -75,7 +75,7 @@ async def get_transfer_fee() -> Balance:
return Balance.from_rao(payment_info["partial_fee"])
- async def do_transfer() -> tuple[bool, str, str]:
+ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
"""
Makes transfer from wallet to destination public key address.
:return: success, block hash, formatted error message
@@ -95,27 +95,32 @@ async def do_transfer() -> tuple[bool, str, str]:
)
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
- return True, "", ""
+ return True, "", "", response
# Otherwise continue with finalization.
if await response.is_success:
block_hash_ = response.block_hash
- return True, block_hash_, ""
+ return True, block_hash_, "", response
else:
- return False, "", format_error_message(await response.error_message)
+ return (
+ False,
+ "",
+ format_error_message(await response.error_message),
+ response,
+ )
# Validate destination address.
if not is_valid_bittensor_address_or_public_key(destination):
err_console.print(
f":cross_mark: [red]Invalid destination SS58 address[/red]:[bold white]\n {destination}[/bold white]"
)
- return False
+ return False, None
console.print(f"[dark_orange]Initiating transfer on network: {subtensor.network}")
# Unlock wallet coldkey.
if not unlock_key(wallet).success:
- return False
+ return False, None
- call_params = {"dest": destination}
+ call_params: dict[str, Optional[Union[str, int]]] = {"dest": destination}
if transfer_all:
call_function = "transfer_all"
if allow_death:
@@ -158,7 +163,7 @@ async def do_transfer() -> tuple[bool, str, str]:
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
f"You can try again with `--allow-death`."
)
- return False
+ return False, None
elif account_balance < (amount + fee) and allow_death:
print_error(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
@@ -166,7 +171,7 @@ async def do_transfer() -> tuple[bool, str, str]:
f" amount: [bright_red]{amount}[/bright_red]\n"
f" for fee: [bright_red]{fee}[/bright_red]"
)
- return False
+ return False, None
# Ask before moving on.
if prompt:
@@ -179,27 +184,15 @@ async def do_transfer() -> tuple[bool, str, str]:
f":warning:[bright_yellow]Transferring is not the same as staking. To instead stake, use "
f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow]:warning:"
):
- return False
+ return False, None
- with console.status(":satellite: Transferring...", spinner="earth") as status:
- success, block_hash, err_msg = await do_transfer()
+ with console.status(":satellite: Transferring...", spinner="earth"):
+ success, block_hash, err_msg, ext_receipt = await do_transfer()
if success:
console.print(":white_heavy_check_mark: [green]Finalized[/green]")
console.print(f"[green]Block Hash: {block_hash}[/green]")
- if subtensor.network == "finney":
- print_verbose("Fetching explorer URLs", status)
- explorer_urls = get_explorer_url_for_network(
- subtensor.network, block_hash, NETWORK_EXPLORER_MAP
- )
- if explorer_urls != {} and explorer_urls:
- console.print(
- f"[green]Opentensor Explorer Link: {explorer_urls.get('opentensor')}[/green]"
- )
- console.print(
- f"[green]Taostats Explorer Link: {explorer_urls.get('taostats')}[/green]"
- )
else:
console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
@@ -212,6 +205,6 @@ async def do_transfer() -> tuple[bool, str, str]:
f"Balance:\n"
f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]"
)
- return True
+ return True, ext_receipt
- return False
+ return False, None
diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py
index 27df7d94c..d554fdbb4 100644
--- a/bittensor_cli/src/bittensor/subtensor_interface.py
+++ b/bittensor_cli/src/bittensor/subtensor_interface.py
@@ -4,6 +4,7 @@
from typing import Optional, Any, Union, TypedDict, Iterable
import aiohttp
+from async_substrate_interface import AsyncExtrinsicReceipt
from async_substrate_interface.async_substrate import (
DiskCachedAsyncSubstrateInterface,
AsyncSubstrateInterface,
@@ -1081,7 +1082,7 @@ async def sign_and_send_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
era: Optional[dict[str, int]] = None,
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
"""
Helper method to sign and submit an extrinsic call to chain.
@@ -1093,7 +1094,10 @@ async def sign_and_send_extrinsic(
:return: (success, error message)
"""
- call_args = {"call": call, "keypair": wallet.coldkey}
+ call_args: dict[str, Union[GenericCall, Keypair, dict[str, int]]] = {
+ "call": call,
+ "keypair": wallet.coldkey,
+ }
if era is not None:
call_args["era"] = era
extrinsic = await self.substrate.create_signed_extrinsic(
@@ -1107,13 +1111,13 @@ async def sign_and_send_extrinsic(
)
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
- return True, ""
+ return True, "", response
if await response.is_success:
- return True, ""
+ return True, "", response
else:
- return False, format_error_message(await response.error_message)
+ return False, format_error_message(await response.error_message), None
except SubstrateRequestException as e:
- return False, format_error_message(e)
+ return False, format_error_message(e), None
async def get_children(self, hotkey, netuid) -> tuple[bool, list, str]:
"""
diff --git a/bittensor_cli/src/bittensor/utils.py b/bittensor_cli/src/bittensor/utils.py
index 80aab6916..5ec5fd56a 100644
--- a/bittensor_cli/src/bittensor/utils.py
+++ b/bittensor_cli/src/bittensor/utils.py
@@ -10,6 +10,7 @@
from functools import partial
import re
+from async_substrate_interface import AsyncExtrinsicReceipt
from bittensor_wallet import Wallet, Keypair
from bittensor_wallet.utils import SS58_FORMAT
from bittensor_wallet.errors import KeyFileError, PasswordError
@@ -507,6 +508,7 @@ def get_explorer_url_for_network(
:return: The explorer url for the given block hash and network
"""
+ # TODO remove
explorer_urls: dict[str, str] = {}
# Will be None if the network is not known. i.e. not in network_map
@@ -1462,3 +1464,30 @@ def get_hotkey_pub_ss58(wallet: Wallet) -> str:
return wallet.hotkeypub.ss58_address
except (KeyFileError, AttributeError):
return wallet.hotkey.ss58_address
+
+
+async def print_extrinsic_id(
+ extrinsic_receipt: Optional[AsyncExtrinsicReceipt],
+) -> None:
+ """
+ Prints the extrinsic identifier to the console. If the substrate attached to the extrinsic receipt is on a finney
+ node, it will also include a link to browse the extrinsic in tao dot app.
+ Args:
+ extrinsic_receipt: AsyncExtrinsicReceipt object from a successful extrinsic submission.
+ """
+ if extrinsic_receipt is None:
+ return
+ substrate = extrinsic_receipt.substrate
+ ext_id = await extrinsic_receipt.get_extrinsic_identifier()
+ if substrate:
+ query = await substrate.rpc_request("system_chainType", [])
+ if query.get("result") == "Live":
+ console.print(
+ f":white_heavy_check_mark:Your extrinsic has been included as {ext_id}: "
+ f"[blue]https://tao.app/extrinsic/{ext_id}[/blue]"
+ )
+ return
+ console.print(
+ f":white_heavy_check_mark:Your extrinsic has been included as {ext_id}"
+ )
+ return
diff --git a/bittensor_cli/src/commands/liquidity/liquidity.py b/bittensor_cli/src/commands/liquidity/liquidity.py
index 60f5c6529..cc25ff1e9 100644
--- a/bittensor_cli/src/commands/liquidity/liquidity.py
+++ b/bittensor_cli/src/commands/liquidity/liquidity.py
@@ -2,6 +2,7 @@
import json
from typing import TYPE_CHECKING, Optional
+from async_substrate_interface import AsyncExtrinsicReceipt
from rich.prompt import Confirm
from rich.table import Column, Table
@@ -11,6 +12,7 @@
console,
err_console,
json_console,
+ print_extrinsic_id,
)
from bittensor_cli.src.bittensor.balances import Balance, fixed_to_float
from bittensor_cli.src.commands.liquidity.utils import (
@@ -36,7 +38,7 @@ async def add_liquidity_extrinsic(
price_high: Balance,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
"""
Adds liquidity to the specified price range.
@@ -60,7 +62,7 @@ async def add_liquidity_extrinsic(
`toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
"""
if not (unlock := unlock_key(wallet)).success:
- return False, unlock.message
+ return False, unlock.message, None
tick_low = price_to_tick(price_low.tao)
tick_high = price_to_tick(price_high.tao)
@@ -94,7 +96,7 @@ async def modify_liquidity_extrinsic(
liquidity_delta: Balance,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
"""Modifies liquidity in liquidity position by adding or removing liquidity from it.
Arguments:
@@ -116,7 +118,7 @@ async def modify_liquidity_extrinsic(
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
"""
if not (unlock := unlock_key(wallet)).success:
- return False, unlock.message
+ return False, unlock.message, None
call = await subtensor.substrate.compose_call(
call_module="Swap",
@@ -145,7 +147,7 @@ async def remove_liquidity_extrinsic(
position_id: int,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
"""Remove liquidity and credit balances back to wallet's hotkey stake.
Arguments:
@@ -166,7 +168,7 @@ async def remove_liquidity_extrinsic(
Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity.
"""
if not (unlock := unlock_key(wallet)).success:
- return False, unlock.message
+ return False, unlock.message, None
call = await subtensor.substrate.compose_call(
call_module="Swap",
@@ -193,7 +195,7 @@ async def toggle_user_liquidity_extrinsic(
enable: bool,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
"""Allow to toggle user liquidity for specified subnet.
Arguments:
@@ -210,7 +212,7 @@ async def toggle_user_liquidity_extrinsic(
- False and an error message if the submission fails or the wallet cannot be unlocked.
"""
if not (unlock := unlock_key(wallet)).success:
- return False, unlock.message
+ return False, unlock.message, None
call = await subtensor.substrate.compose_call(
call_module="Swap",
@@ -232,16 +234,16 @@ async def add_liquidity(
wallet: "Wallet",
hotkey_ss58: str,
netuid: Optional[int],
- liquidity: Optional[float],
- price_low: Optional[float],
- price_high: Optional[float],
+ liquidity: Balance,
+ price_low: Balance,
+ price_high: Balance,
prompt: bool,
json_output: bool,
) -> tuple[bool, str]:
"""Add liquidity position to provided subnet."""
# Check wallet access
- if not unlock_key(wallet).success:
- return False
+ if not (ulw := unlock_key(wallet)).success:
+ return False, ulw.message
# Check that the subnet exists.
if not await subtensor.subnet_exists(netuid=netuid):
@@ -260,7 +262,7 @@ async def add_liquidity(
if not Confirm.ask("Would you like to continue?"):
return False, "User cancelled operation."
- success, message = await add_liquidity_extrinsic(
+ success, message, ext_receipt = await add_liquidity_extrinsic(
subtensor=subtensor,
wallet=wallet,
hotkey_ss58=hotkey_ss58,
@@ -269,8 +271,14 @@ async def add_liquidity(
price_low=price_low,
price_high=price_high,
)
+ await print_extrinsic_id(ext_receipt)
+ ext_id = await ext_receipt.get_extrinsic_identifier()
if json_output:
- json_console.print(json.dumps({"success": success, "message": message}))
+ json_console.print(
+ json.dumps(
+ {"success": success, "message": message, "extrinsic_identifier": ext_id}
+ )
+ )
else:
if success:
console.print(
@@ -278,6 +286,7 @@ async def add_liquidity(
)
else:
err_console.print(f"[red]Error: {message}[/red]")
+ return success, message
async def get_liquidity_list(
@@ -535,11 +544,12 @@ async def remove_liquidity(
success, msg, positions = await get_liquidity_list(subtensor, wallet, netuid)
if not success:
if json_output:
- return json_console.print(
- {"success": False, "err_msg": msg, "positions": positions}
+ json_console.print_json(
+ data={"success": False, "err_msg": msg, "positions": positions}
)
else:
return err_console.print(f"Error: {msg}")
+ return False, msg
else:
position_ids = [p.id for p in positions]
else:
@@ -568,16 +578,21 @@ async def remove_liquidity(
]
)
if not json_output:
- for (success, msg), posid in zip(results, position_ids):
+ for (success, msg, ext_receipt), posid in zip(results, position_ids):
if success:
+ await print_extrinsic_id(ext_receipt)
console.print(f"[green] Position {posid} has been removed.")
else:
err_console.print(f"[red] Error removing {posid}: {msg}")
else:
json_table = {}
- for (success, msg), posid in zip(results, position_ids):
- json_table[posid] = {"success": success, "err_msg": msg}
- json_console.print(json.dumps(json_table))
+ for (success, msg, ext_receipt), posid in zip(results, position_ids):
+ json_table[posid] = {
+ "success": success,
+ "err_msg": msg,
+ "extrinsic_identifier": await ext_receipt.get_extrinsic_identifier(),
+ }
+ json_console.print_json(data=json_table)
async def modify_liquidity(
@@ -586,7 +601,7 @@ async def modify_liquidity(
hotkey_ss58: str,
netuid: int,
position_id: int,
- liquidity_delta: Optional[float],
+ liquidity_delta: Balance,
prompt: Optional[bool] = None,
json_output: bool = False,
) -> bool:
@@ -611,7 +626,7 @@ async def modify_liquidity(
if not Confirm.ask("Would you like to continue?"):
return False
- success, msg = await modify_liquidity_extrinsic(
+ success, msg, ext_receipt = await modify_liquidity_extrinsic(
subtensor=subtensor,
wallet=wallet,
hotkey_ss58=hotkey_ss58,
@@ -620,9 +635,14 @@ async def modify_liquidity(
liquidity_delta=liquidity_delta,
)
if json_output:
- json_console.print(json.dumps({"success": success, "err_msg": msg}))
+ ext_id = await ext_receipt.get_extrinsic_identifier() if success else None
+ json_console.print_json(
+ data={"success": success, "err_msg": msg, "extrinsic_identifier": ext_id}
+ )
else:
if success:
+ await print_extrinsic_id(ext_receipt)
console.print(f"[green] Position {position_id} has been modified.")
else:
err_console.print(f"[red] Error modifying {position_id}: {msg}")
+ return success
diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py
index 18a507c6d..9f3ffc0d0 100644
--- a/bittensor_cli/src/commands/stake/add.py
+++ b/bittensor_cli/src/commands/stake/add.py
@@ -4,6 +4,8 @@
from functools import partial
from typing import TYPE_CHECKING, Optional
+
+from async_substrate_interface import AsyncExtrinsicReceipt
from rich.table import Table
from rich.prompt import Confirm, Prompt
@@ -21,6 +23,7 @@
unlock_key,
json_console,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
from bittensor_wallet import Wallet
@@ -112,7 +115,7 @@ async def safe_stake_extrinsic(
hotkey_ss58_: str,
price_limit: Balance,
status=None,
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
err_out = partial(print_error, status=status)
failure_prelude = (
f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}"
@@ -153,15 +156,16 @@ async def safe_stake_extrinsic(
else:
err_msg = f"{failure_prelude} with error: {format_error_message(e)}"
err_out("\n" + err_msg)
- return False, err_msg
+ return False, err_msg, None
if not await response.is_success:
err_msg = f"{failure_prelude} with error: {format_error_message(await response.error_message)}"
err_out("\n" + err_msg)
- return False, err_msg
+ return False, err_msg, None
else:
if json_output:
# the rest of this checking is not necessary if using json_output
- return True, ""
+ return True, "", response
+ await print_extrinsic_id(response)
block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
@@ -199,11 +203,11 @@ async def safe_stake_extrinsic(
f":arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
)
- return True, ""
+ return True, "", response
async def stake_extrinsic(
netuid_i, amount_, current, staking_address_ss58, status=None
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
err_out = partial(print_error, status=status)
current_balance, next_nonce, call = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address),
@@ -231,16 +235,17 @@ async def stake_extrinsic(
except SubstrateRequestException as e:
err_msg = f"{failure_prelude} with error: {format_error_message(e)}"
err_out("\n" + err_msg)
- return False, err_msg
+ return False, err_msg, None
else:
if not await response.is_success:
err_msg = f"{failure_prelude} with error: {format_error_message(await response.error_message)}"
err_out("\n" + err_msg)
- return False, err_msg
+ return False, err_msg, None
else:
if json_output:
# the rest of this is not necessary if using json_output
- return True, ""
+ return True, "", response
+ await print_extrinsic_id(response)
new_block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(
@@ -269,7 +274,7 @@ async def stake_extrinsic(
f":arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
)
- return True, ""
+ return True, "", response
netuids = (
netuids if netuids is not None else await subtensor.get_all_subnet_netuids()
@@ -470,15 +475,23 @@ async def stake_extrinsic(
}
successes = defaultdict(dict)
error_messages = defaultdict(dict)
+ extrinsic_ids = 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 (ni, staking_address), coroutine in stake_coroutines.items():
- success, er_msg = await coroutine
+ success, er_msg, ext_receipt = await coroutine
successes[ni][staking_address] = success
error_messages[ni][staking_address] = er_msg
+ extrinsic_ids[ni][
+ staking_address
+ ] = await ext_receipt.get_extrinsic_identifier()
if json_output:
- json_console.print(
- json.dumps({"staking_success": successes, "error_messages": error_messages})
+ json_console.print_json(
+ data={
+ "staking_success": successes,
+ "error_messages": error_messages,
+ "extrinsic_ids": extrinsic_ids,
+ }
)
diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py
index d01e8d147..d50ecc65a 100644
--- a/bittensor_cli/src/commands/stake/children_hotkeys.py
+++ b/bittensor_cli/src/commands/stake/children_hotkeys.py
@@ -22,6 +22,7 @@
unlock_key,
json_console,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
@@ -59,7 +60,7 @@ async def set_children_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = False,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
"""
Sets children hotkeys with proportions assigned from the parent.
@@ -74,7 +75,7 @@ async def set_children_extrinsic(
`True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
:param: prompt: If `True`, the call waits for confirmation from the user before proceeding.
- :return: A tuple containing a success flag and an optional error message.
+ :return: A tuple containing a success flag, an optional error message, and the extrinsic identifier
"""
# Check if all children are being revoked
all_revoked = len(children_with_proportions) == 0
@@ -87,7 +88,7 @@ async def set_children_extrinsic(
if not Confirm.ask(
f"Do you want to revoke all children hotkeys for hotkey {hotkey} on netuid {netuid}?"
):
- return False, "Operation Cancelled"
+ return False, "Operation Cancelled", None
else:
if not Confirm.ask(
"Do you want to set children hotkeys:\n[bold white]{}[/bold white]?".format(
@@ -97,11 +98,11 @@ async def set_children_extrinsic(
)
)
):
- return False, "Operation Cancelled"
+ return False, "Operation Cancelled", None
# Decrypt coldkey.
if not (unlock_status := unlock_key(wallet, print_out=False)).success:
- return False, unlock_status.message
+ return False, unlock_status.message, ""
with console.status(
f":satellite: {operation} on [white]{subtensor.network}[/white] ..."
@@ -120,7 +121,7 @@ async def set_children_extrinsic(
"netuid": netuid,
},
)
- success, error_message = await subtensor.sign_and_send_extrinsic(
+ success, error_message, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization
)
@@ -128,17 +129,20 @@ async def set_children_extrinsic(
return (
True,
f"Not waiting for finalization or inclusion. {operation} initiated.",
+ None,
)
if success:
- if wait_for_inclusion:
- console.print(":white_heavy_check_mark: [green]Included[/green]")
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
+ modifier = "included"
if wait_for_finalization:
console.print(":white_heavy_check_mark: [green]Finalized[/green]")
- return True, f"Successfully {operation.lower()} and Finalized."
+ modifier = "finalized"
+ return True, f"{operation} successfully {modifier}.", ext_id
else:
err_console.print(f":cross_mark: [red]Failed[/red]: {error_message}")
- return False, error_message
+ return False, error_message, None
async def set_childkey_take_extrinsic(
@@ -150,7 +154,7 @@ async def set_childkey_take_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = True,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
"""
Sets childkey take.
@@ -165,7 +169,7 @@ async def set_childkey_take_extrinsic(
`True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
:param: prompt: If `True`, the call waits for confirmation from the user before proceeding.
- :return: A tuple containing a success flag and an optional error message.
+ :return: A tuple containing a success flag, an optional error message, and an optional extrinsic identifier
"""
# Ask before moving on.
@@ -173,11 +177,11 @@ async def set_childkey_take_extrinsic(
if not Confirm.ask(
f"Do you want to set childkey take to: [bold white]{take * 100}%[/bold white]?"
):
- return False, "Operation Cancelled"
+ return False, "Operation Cancelled", None
# Decrypt coldkey.
if not (unlock_status := unlock_key(wallet, print_out=False)).success:
- return False, unlock_status.message
+ return False, unlock_status.message, None
with console.status(
f":satellite: Setting childkey take on [white]{subtensor.network}[/white] ..."
@@ -186,7 +190,7 @@ async def set_childkey_take_extrinsic(
if 0 <= take <= 0.18:
take_u16 = float_to_u16(take)
else:
- return False, "Invalid take value"
+ return False, "Invalid take value", None
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -197,7 +201,11 @@ async def set_childkey_take_extrinsic(
"netuid": netuid,
},
)
- success, error_message = await subtensor.sign_and_send_extrinsic(
+ (
+ success,
+ error_message,
+ ext_receipt,
+ ) = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization
)
@@ -205,30 +213,34 @@ async def set_childkey_take_extrinsic(
return (
True,
"Not waiting for finalization or inclusion. Set childkey take initiated.",
+ None,
)
if success:
- if wait_for_inclusion:
- console.print(":white_heavy_check_mark: [green]Included[/green]")
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
+ modifier = "included"
if wait_for_finalization:
+ modifier = "finalized"
console.print(":white_heavy_check_mark: [green]Finalized[/green]")
# bittensor.logging.success(
# prefix="Setting childkey take",
# suffix="Finalized: " + str(success),
# )
- return True, "Successfully set childkey take and Finalized."
+ return True, f"Successfully {modifier} childkey take", ext_id
else:
console.print(f":cross_mark: [red]Failed[/red]: {error_message}")
# bittensor.logging.warning(
# prefix="Setting childkey take",
# suffix="Failed: " + str(error_message),
# )
- return False, error_message
+ return False, error_message, None
except SubstrateRequestException as e:
return (
False,
f"Exception occurred while setting childkey take: {format_error_message(e)}",
+ None,
)
@@ -519,7 +531,7 @@ async def set_children(
children_with_proportions = list(zip(proportions, children))
successes = {}
if netuid is not None:
- success, message = await set_children_extrinsic(
+ success, message, ext_id = await set_children_extrinsic(
subtensor=subtensor,
wallet=wallet,
netuid=netuid,
@@ -534,6 +546,7 @@ async def set_children(
"error": message,
"completion_block": None,
"set_block": None,
+ "extrinsic_identifier": ext_id,
}
# Result
if success:
@@ -561,7 +574,7 @@ async def set_children(
if netuid_ == 0: # dont include root network
continue
console.print(f"Setting children on netuid {netuid_}.")
- success, message = await set_children_extrinsic(
+ success, message, ext_id = await set_children_extrinsic(
subtensor=subtensor,
wallet=wallet,
netuid=netuid_,
@@ -579,6 +592,7 @@ async def set_children(
"error": message,
"completion_block": completion_block,
"set_block": current_block,
+ "extrinsic_identifier": ext_id,
}
console.print(
f"Your childkey request for netuid {netuid_} has been submitted. It will be completed around "
@@ -605,7 +619,7 @@ async def revoke_children(
"""
dict_output = {}
if netuid is not None:
- success, message = await set_children_extrinsic(
+ success, message, ext_id = await set_children_extrinsic(
subtensor=subtensor,
wallet=wallet,
netuid=netuid,
@@ -620,6 +634,7 @@ async def revoke_children(
"error": message,
"set_block": None,
"completion_block": None,
+ "extrinsic_identifier": ext_id,
}
# Result
@@ -644,10 +659,10 @@ async def revoke_children(
if netuid_ == 0: # dont include root network
continue
console.print(f"Revoking children from netuid {netuid_}.")
- success, message = await set_children_extrinsic(
+ success, message, ext_id = await set_children_extrinsic(
subtensor=subtensor,
wallet=wallet,
- netuid=netuid,
+ netuid=netuid, # TODO should this be able to allow netuid = None ?
hotkey=get_hotkey_pub_ss58(wallet),
children_with_proportions=[],
prompt=prompt,
@@ -659,6 +674,7 @@ async def revoke_children(
"error": message,
"set_block": None,
"completion_block": None,
+ "extrinsic_identifier": ext_id,
}
if success:
current_block, completion_block = await get_childkey_completion_block(
@@ -688,12 +704,12 @@ async def childkey_take(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
prompt: bool = True,
-) -> list[tuple[Optional[int], bool]]:
+) -> list[tuple[Optional[int], bool, Optional[str]]]:
"""
Get or Set childkey take.
Returns:
- List of (netuid, success) for specified netuid (or all) and their success in setting take
+ List of (netuid, success, extrinsic identifier) for specified netuid (or all) and their success in setting take
"""
def validate_take_value(take_value: float) -> bool:
@@ -741,9 +757,11 @@ async def chk_all_subnets(ss58):
console.print(table)
- async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
+ async def set_chk_take_subnet(
+ subnet: int, chk_take: float
+ ) -> tuple[bool, Optional[str]]:
"""Set the childkey take for a single subnet"""
- success, message = await set_childkey_take_extrinsic(
+ success, message, ext_id = await set_childkey_take_extrinsic(
subtensor=subtensor,
wallet=wallet,
netuid=subnet,
@@ -759,12 +777,12 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
console.print(
f"The childkey take for {get_hotkey_pub_ss58(wallet)} is now set to {take * 100:.2f}%."
)
- return True
+ return True, ext_id
else:
console.print(
f":cross_mark:[red] Unable to set childkey take.[/red] {message}"
)
- return False
+ return False, ext_id
# Print childkey take for other user and return (dont offer to change take rate)
wallet_hk = get_hotkey_pub_ss58(wallet)
@@ -778,7 +796,7 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
console.print(
f"Hotkey {hotkey} not associated with wallet {wallet.name}."
)
- return [(netuid, False)]
+ return [(netuid, False, None)]
else:
# show child hotkey take on all subnets
await chk_all_subnets(hotkey)
@@ -786,12 +804,12 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
console.print(
f"Hotkey {hotkey} not associated with wallet {wallet.name}."
)
- return [(netuid, False)]
+ return [(netuid, False, None)]
# Validate child SS58 addresses
if not take:
if not Confirm.ask("Would you like to change the child take?"):
- return [(netuid, False)]
+ return [(netuid, False, None)]
new_take_value = -1.0
while not validate_take_value(new_take_value):
new_take_value = FloatPrompt.ask(
@@ -800,22 +818,21 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
take = new_take_value
else:
if not validate_take_value(take):
- return [(netuid, False)]
+ return [(netuid, False, None)]
if netuid:
- return [(netuid, await set_chk_take_subnet(subnet=netuid, chk_take=take))]
+ success, ext_id = await set_chk_take_subnet(subnet=netuid, chk_take=take)
+ return [(netuid, success, ext_id)]
else:
new_take_netuids = IntPrompt.ask(
"Enter netuid (leave blank for all)", default=None, show_default=True
)
if new_take_netuids:
- return [
- (
- new_take_netuids,
- await set_chk_take_subnet(subnet=new_take_netuids, chk_take=take),
- )
- ]
+ success, ext_id = await set_chk_take_subnet(
+ subnet=new_take_netuids, chk_take=take
+ )
+ return [(new_take_netuids, success, ext_id)]
else:
netuids = await subtensor.get_all_subnet_netuids()
@@ -823,8 +840,8 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
for netuid_ in netuids:
if netuid_ == 0:
continue
- console.print(f"Sending to netuid {netuid_} take of {take * 100:.2f}%")
- result = await set_childkey_take_extrinsic(
+ console.print(f"Setting take of {take * 100:.2f}% on netuid {netuid_}.")
+ result, _, ext_id = await set_childkey_take_extrinsic(
subtensor=subtensor,
wallet=wallet,
netuid=netuid_,
@@ -834,7 +851,7 @@ async def set_chk_take_subnet(subnet: int, chk_take: float) -> bool:
wait_for_inclusion=True,
wait_for_finalization=False,
)
- output_list.append((netuid_, result))
+ output_list.append((netuid_, result, ext_id))
console.print(
f":white_heavy_check_mark: [green]Sent childkey take of {take * 100:.2f}% to all subnets.[/green]"
)
diff --git a/bittensor_cli/src/commands/stake/move.py b/bittensor_cli/src/commands/stake/move.py
index b4360ffdf..1443d611e 100644
--- a/bittensor_cli/src/commands/stake/move.py
+++ b/bittensor_cli/src/commands/stake/move.py
@@ -17,6 +17,7 @@
get_subnet_name,
unlock_key,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if TYPE_CHECKING:
@@ -436,12 +437,12 @@ async def move_stake(
era: int,
interactive_selection: bool = False,
prompt: bool = True,
-) -> bool:
+) -> tuple[bool, str]:
if interactive_selection:
try:
selection = await stake_move_transfer_selection(subtensor, wallet)
except ValueError:
- return False
+ return False, ""
origin_hotkey = selection["origin_hotkey"]
origin_netuid = selection["origin_netuid"]
amount = selection["amount"]
@@ -472,7 +473,7 @@ async def move_stake(
f"in Netuid: "
f"[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{origin_netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
)
- return False
+ return False, ""
console.print(
f"\nOrigin Netuid: "
@@ -507,7 +508,7 @@ async def move_stake(
f" < Moving amount: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]"
f"{amount_to_move_as_balance}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]"
)
- return False
+ return False, ""
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -545,13 +546,13 @@ async def move_stake(
extrinsic_fee=extrinsic_fee,
)
except ValueError:
- return False
+ return False, ""
if not Confirm.ask("Would you like to continue?"):
- return False
+ return False, ""
# Perform moving operation.
if not unlock_key(wallet).success:
- return False
+ return False, ""
with console.status(
f"\n:satellite: Moving [blue]{amount_to_move_as_balance}[/blue] from [blue]{origin_hotkey}[/blue] on netuid: "
f"[blue]{origin_netuid}[/blue] \nto "
@@ -563,17 +564,19 @@ async def move_stake(
response = await subtensor.substrate.submit_extrinsic(
extrinsic, wait_for_inclusion=True, wait_for_finalization=False
)
+ await print_extrinsic_id(response)
+ ext_id = await response.get_extrinsic_identifier()
if not prompt:
console.print(":white_heavy_check_mark: [green]Sent[/green]")
- return True
+ return True, ext_id
else:
if not await response.is_success:
err_console.print(
f"\n:cross_mark: [red]Failed[/red] with error:"
f" {format_error_message(await response.error_message)}"
)
- return False
+ return False, ""
else:
console.print(
":white_heavy_check_mark: [dark_sea_green3]Stake moved.[/dark_sea_green3]"
@@ -605,7 +608,7 @@ async def move_stake(
f"Destination Stake:\n [blue]{destination_stake_balance}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_destination_stake_balance}"
)
- return True
+ return True, ext_id
async def transfer_stake(
@@ -620,7 +623,7 @@ async def transfer_stake(
interactive_selection: bool = False,
stake_all: bool = False,
prompt: bool = True,
-) -> bool:
+) -> tuple[bool, str]:
"""Transfers stake from one network to another.
Args:
@@ -653,11 +656,11 @@ async def transfer_stake(
)
if not dest_exists:
err_console.print(f"[red]Subnet {dest_netuid} does not exist[/red]")
- return False
+ return False, ""
if not origin_exists:
err_console.print(f"[red]Subnet {origin_netuid} does not exist[/red]")
- return False
+ return False, ""
# Get current stake balances
with console.status(f"Retrieving stake data from {subtensor.network}..."):
@@ -676,7 +679,7 @@ async def transfer_stake(
err_console.print(
f"[red]No stake found for hotkey: {origin_hotkey} on netuid: {origin_netuid}[/red]"
)
- return False
+ return False, ""
if amount:
amount_to_transfer = Balance.from_tao(amount).set_unit(origin_netuid)
@@ -694,7 +697,7 @@ async def transfer_stake(
f"Stake balance: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_stake}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] < "
f"Transfer amount: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{amount_to_transfer}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
)
- return False
+ return False, ""
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -732,14 +735,14 @@ async def transfer_stake(
extrinsic_fee=extrinsic_fee,
)
except ValueError:
- return False
+ return False, ""
if not Confirm.ask("Would you like to continue?"):
- return False
+ return False, ""
# Perform transfer operation
if not unlock_key(wallet).success:
- return False
+ return False, ""
with console.status("\n:satellite: Transferring stake ..."):
extrinsic = await subtensor.substrate.create_signed_extrinsic(
@@ -749,17 +752,19 @@ async def transfer_stake(
response = await subtensor.substrate.submit_extrinsic(
extrinsic, wait_for_inclusion=True, wait_for_finalization=False
)
+ ext_id = await response.get_extrinsic_identifier()
+ await print_extrinsic_id(extrinsic)
if not prompt:
console.print(":white_heavy_check_mark: [green]Sent[/green]")
- return True
+ return True, ext_id
if not await response.is_success:
err_console.print(
f":cross_mark: [red]Failed[/red] with error: "
f"{format_error_message(await response.error_message)}"
)
- return False
+ return False, ""
# Get and display new stake balances
new_stake, new_dest_stake = await asyncio.gather(
@@ -783,7 +788,7 @@ async def transfer_stake(
f"Destination Stake:\n [blue]{current_dest_stake}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_dest_stake}"
)
- return True
+ return True, ext_id
async def swap_stake(
@@ -798,7 +803,7 @@ async def swap_stake(
prompt: bool = True,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
-) -> bool:
+) -> tuple[bool, str]:
"""Swaps stake between subnets while keeping the same coldkey-hotkey pair ownership.
Args:
@@ -833,11 +838,11 @@ async def swap_stake(
)
if not dest_exists:
err_console.print(f"[red]Subnet {destination_netuid} does not exist[/red]")
- return False
+ return False, ""
if not origin_exists:
err_console.print(f"[red]Subnet {origin_netuid} does not exist[/red]")
- return False
+ return False, ""
# Get current stake balances
with console.status(f"Retrieving stake data from {subtensor.network}..."):
@@ -864,7 +869,7 @@ async def swap_stake(
f"Stake balance: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_stake}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] < "
f"Swap amount: [{COLOR_PALETTE.S.STAKE_AMOUNT}]{amount_to_swap}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
)
- return False
+ return False, ""
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -901,14 +906,14 @@ async def swap_stake(
extrinsic_fee=extrinsic_fee,
)
except ValueError:
- return False
+ return False, ""
if not Confirm.ask("Would you like to continue?"):
- return False
+ return False, ""
# Perform swap operation
if not unlock_key(wallet).success:
- return False
+ return False, ""
with console.status(
f"\n:satellite: Swapping stake from netuid [blue]{origin_netuid}[/blue] "
@@ -923,17 +928,19 @@ async def swap_stake(
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)
+ ext_id = await response.get_extrinsic_identifier()
+ await print_extrinsic_id(response)
if not prompt:
console.print(":white_heavy_check_mark: [green]Sent[/green]")
- return True
+ return True, ext_id
if not await response.is_success:
err_console.print(
f":cross_mark: [red]Failed[/red] with error: "
f"{format_error_message(await response.error_message)}"
)
- return False
+ return False, ""
# Get and display new stake balances
new_stake, new_dest_stake = await asyncio.gather(
@@ -957,4 +964,4 @@ async def swap_stake(
f"Destination Stake:\n [blue]{current_dest_stake}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_dest_stake}"
)
- return True
+ return True, ext_id
diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py
index ecec77fa5..b4b6bbeb1 100644
--- a/bittensor_cli/src/commands/stake/remove.py
+++ b/bittensor_cli/src/commands/stake/remove.py
@@ -4,6 +4,7 @@
from typing import TYPE_CHECKING, Optional
+from async_substrate_interface import AsyncExtrinsicReceipt
from bittensor_wallet import Wallet
from rich.prompt import Confirm, Prompt
from rich.table import Table
@@ -23,6 +24,7 @@
unlock_key,
json_console,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if TYPE_CHECKING:
@@ -134,7 +136,8 @@ async def unstake(
skip_remaining_subnets = False
if len(netuids) > 1 and not amount:
console.print(
- "[dark_sea_green3]Tip: Enter 'q' any time to stop going over remaining subnets and process current unstakes.\n"
+ "[dark_sea_green3]Tip: Enter 'q' any time to stop going over "
+ "remaining subnets and process current unstakes.\n"
)
# Iterate over hotkeys and netuids to collect unstake operations
@@ -335,7 +338,8 @@ async def unstake(
func = _unstake_extrinsic
specific_args = {"current_stake": op["current_stake_balance"]}
- suc = await func(**common_args, **specific_args)
+ suc, ext_receipt = await func(**common_args, **specific_args)
+ ext_id = await ext_receipt.get_extrinsic_identifier() if suc else None
successes.append(
{
@@ -343,6 +347,7 @@ async def unstake(
"hotkey_ss58": op["hotkey_ss58"],
"unstake_amount": op["amount_to_unstake"].tao,
"success": suc,
+ "extrinsic_identifier": ext_id,
}
)
@@ -350,7 +355,7 @@ async def unstake(
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]Unstaking operations completed."
)
if json_output:
- json_console.print(json.dumps(successes))
+ json_console.print_json(data=successes)
return True
@@ -533,7 +538,7 @@ async def unstake_all(
successes = {}
with console.status("Unstaking all stakes...") as status:
for hotkey_ss58 in hotkey_ss58s:
- successes[hotkey_ss58] = await _unstake_all_extrinsic(
+ success, ext_receipt = await _unstake_all_extrinsic(
wallet=wallet,
subtensor=subtensor,
hotkey_ss58=hotkey_ss58,
@@ -542,6 +547,11 @@ async def unstake_all(
status=status,
era=era,
)
+ ext_id = await ext_receipt.get_extrinsic_identifier() if successes else None
+ successes[hotkey_ss58] = {
+ "success": success,
+ "extrinsic_identifier": ext_id,
+ }
if json_output:
return json_console.print(json.dumps({"success": successes}))
@@ -556,7 +566,7 @@ async def _unstake_extrinsic(
hotkey_ss58: str,
status=None,
era: int = 3,
-) -> bool:
+) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""Execute a standard unstake extrinsic.
Args:
@@ -604,8 +614,9 @@ async def _unstake_extrinsic(
f"{failure_prelude} with error: "
f"{format_error_message(await response.error_message)}"
)
- return False
+ return False, None
# Fetch latest balance and stake
+ await print_extrinsic_id(response)
block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
@@ -625,11 +636,11 @@ async def _unstake_extrinsic(
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
f" Stake:\n [blue]{current_stake}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}"
)
- return True
+ return True, response
except Exception as e:
err_out(f"{failure_prelude} with error: {str(e)}")
- return False
+ return False, None
async def _safe_unstake_extrinsic(
@@ -642,7 +653,7 @@ async def _safe_unstake_extrinsic(
allow_partial_stake: bool,
status=None,
era: int = 3,
-) -> bool:
+) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""Execute a safe unstake extrinsic with price limit.
Args:
@@ -708,14 +719,14 @@ async def _safe_unstake_extrinsic(
)
else:
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
- return False
+ return False, None
if not await response.is_success:
err_out(
f"\n{failure_prelude} with error: {format_error_message(await response.error_message)}"
)
- return False
-
+ return False, None
+ await print_extrinsic_id(response)
block_hash = await subtensor.substrate.get_chain_head()
new_balance, new_stake = await asyncio.gather(
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
@@ -745,7 +756,7 @@ async def _safe_unstake_extrinsic(
f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] "
f"Stake:\n [blue]{current_stake}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}"
)
- return True
+ return True, response
async def _unstake_all_extrinsic(
@@ -756,7 +767,7 @@ async def _unstake_all_extrinsic(
unstake_all_alpha: bool,
status=None,
era: int = 3,
-) -> None:
+) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""Execute an unstake all extrinsic.
Args:
@@ -774,7 +785,7 @@ async def _unstake_all_extrinsic(
if status:
status.update(
- f"\n:satellite: {'Unstaking all Alpha stakes' if unstake_all_alpha else 'Unstaking all stakes'} from {hotkey_name} ..."
+ f"\n:satellite: Unstaking all {'Alpha ' if unstake_all_alpha else ''}stakes from {hotkey_name} ..."
)
block_hash = await subtensor.substrate.get_chain_head()
@@ -817,7 +828,9 @@ async def _unstake_all_extrinsic(
f"{failure_prelude} with error: "
f"{format_error_message(await response.error_message)}"
)
- return
+ return False, None
+ else:
+ await print_extrinsic_id(response)
# Fetch latest balance and stake
block_hash = await subtensor.substrate.get_chain_head()
@@ -855,9 +868,11 @@ async def _unstake_all_extrinsic(
f"[blue]{previous_root_stake}[/blue] :arrow_right: "
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_root_stake}"
)
+ return True, response
except Exception as e:
err_out(f"{failure_prelude} with error: {str(e)}")
+ return False, None
async def _get_extrinsic_fee(
diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py
index d8571f3f6..ef4ae59de 100644
--- a/bittensor_cli/src/commands/subnets/subnets.py
+++ b/bittensor_cli/src/commands/subnets/subnets.py
@@ -37,6 +37,7 @@
blocks_to_duration,
json_console,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if TYPE_CHECKING:
@@ -54,7 +55,7 @@ async def register_subnetwork_extrinsic(
wait_for_inclusion: bool = False,
wait_for_finalization: bool = True,
prompt: bool = False,
-) -> tuple[bool, Optional[int]]:
+) -> tuple[bool, Optional[int], Optional[str]]:
"""Registers a new subnetwork.
wallet (bittensor.wallet):
@@ -66,9 +67,11 @@ async def register_subnetwork_extrinsic(
prompt (bool):
If true, the call waits for confirmation from the user before proceeding.
Returns:
- success (bool):
- Flag is ``true`` if extrinsic was finalized or included in the block.
- If we did not wait for finalization / inclusion, the response is ``true``.
+ tuple including:
+ success: Flag is `True` if extrinsic was finalized or included in the block.
+ If we did not wait for finalization/inclusion, the response is `True`.
+ error_message: Optional error message.
+ extrinsic_identifier: Optional extrinsic identifier, if the extrinsic was included.
"""
async def _find_event_attributes_in_extrinsic_receipt(
@@ -103,7 +106,7 @@ async def _find_event_attributes_in_extrinsic_receipt(
f"[{COLOR_PALETTE['POOLS']['TAO']}]{sn_burn_cost}[{COLOR_PALETTE['POOLS']['TAO']}] "
f"to register a subnet."
)
- return False, None
+ return False, None, None
if prompt:
console.print(
@@ -112,7 +115,7 @@ async def _find_event_attributes_in_extrinsic_receipt(
if not Confirm.ask(
f"Do you want to burn [{COLOR_PALETTE['POOLS']['TAO']}]{sn_burn_cost} to register a subnet?"
):
- return False, None
+ return False, None, None
call_params = {
"hotkey": get_hotkey_pub_ss58(wallet),
@@ -157,10 +160,10 @@ async def _find_event_attributes_in_extrinsic_receipt(
f"[red]Error:[/red] Identity field [white]{field}[/white] must be <= {max_size} bytes.\n"
f"Value '{value.decode()}' is {len(value)} bytes."
)
- return False, None
+ return False, None, None
if not unlock_key(wallet).success:
- return False, None
+ return False, None, None
with console.status(":satellite: Registering subnet...", spinner="earth"):
substrate = subtensor.substrate
@@ -181,24 +184,26 @@ async def _find_event_attributes_in_extrinsic_receipt(
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
- return True, None
+ return True, None, None
if not await response.is_success:
err_console.print(
f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}"
)
await asyncio.sleep(0.5)
- return False, None
+ return False, None, None
# Successful registration, final check for membership
else:
attributes = await _find_event_attributes_in_extrinsic_receipt(
response, "NetworkAdded"
)
+ await print_extrinsic_id(response)
+ ext_id = await response.get_extrinsic_identifier()
console.print(
f":white_heavy_check_mark: [dark_sea_green3]Registered subnetwork with netuid: {attributes[0]}"
)
- return True, int(attributes[0])
+ return True, int(attributes[0]), ext_id
# commands
@@ -1486,13 +1491,17 @@ async def create(
"""Register a subnetwork"""
# Call register command.
- success, netuid = await register_subnetwork_extrinsic(
+ success, netuid, ext_id = await register_subnetwork_extrinsic(
subtensor, wallet, subnet_identity, prompt=prompt
)
if json_output:
# technically, netuid can be `None`, but only if not wait for finalization/inclusion. However, as of present
# (2025/04/03), we always use the default `wait_for_finalization=True`, so it will always have a netuid.
- json_console.print(json.dumps({"success": success, "netuid": netuid}))
+ json_console.print(
+ json.dumps(
+ {"success": success, "netuid": netuid, "extrinsic_identifier": ext_id}
+ )
+ )
return success
if success and prompt:
# Prompt for user to set identity.
@@ -1584,9 +1593,11 @@ async def register(
err_console.print(f"[red]Subnet {netuid} does not exist[/red]")
if json_output:
json_console.print(
- json.dumps(
- {"success": False, "error": f"Subnet {netuid} does not exist"}
- )
+ data={
+ "success": False,
+ "msg": f"Subnet {netuid} does not exist",
+ "extrinsic_identifier": None,
+ }
)
return
@@ -1604,9 +1615,12 @@ async def register(
# Check balance is sufficient
if balance < current_recycle:
- err_console.print(
- f"[red]Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO[/red]"
- )
+ err_msg = f"Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO"
+ err_console.print(f"[red]{err_msg}[/red]")
+ if json_output:
+ json_console.print_json(
+ data={"success": False, "msg": err_msg, "extrinsic_identifier": None}
+ )
return
if prompt and not json_output:
@@ -1671,9 +1685,9 @@ async def register(
return
if netuid == 0:
- success, msg = await root_register_extrinsic(subtensor, wallet=wallet)
+ success, msg, ext_id = await root_register_extrinsic(subtensor, wallet=wallet)
else:
- success, msg = await burned_register_extrinsic(
+ success, msg, ext_id = await burned_register_extrinsic(
subtensor,
wallet=wallet,
netuid=netuid,
@@ -1681,7 +1695,9 @@ async def register(
era=era,
)
if json_output:
- json_console.print(json.dumps({"success": success, "msg": msg}))
+ json_console.print(
+ json.dumps({"success": success, "msg": msg, "extrinsic_identifier": ext_id})
+ )
else:
if not success:
err_console.print(f"Failure: {msg}")
@@ -2207,12 +2223,12 @@ async def set_identity(
netuid: int,
subnet_identity: dict,
prompt: bool = False,
-) -> bool:
+) -> tuple[bool, Optional[str]]:
"""Set identity information for a subnet"""
if not await subtensor.subnet_exists(netuid):
err_console.print(f"Subnet {netuid} does not exist")
- return False
+ return False, None
identity_data = {
"netuid": netuid,
@@ -2227,13 +2243,13 @@ async def set_identity(
}
if not unlock_key(wallet).success:
- return False
+ return False, None
if prompt:
if not Confirm.ask(
"Are you sure you want to set subnet's identity? This is subject to a fee."
):
- return False
+ return False, None
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -2245,12 +2261,15 @@ async def set_identity(
" :satellite: [dark_sea_green3]Setting subnet identity on-chain...",
spinner="earth",
):
- success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
+ call, wallet
+ )
if not success:
err_console.print(f"[red]:cross_mark: Failed![/red] {err_msg}")
- return False
-
+ return False, None
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
console.print(
":white_heavy_check_mark: [dark_sea_green3]Successfully set subnet identity\n"
)
@@ -2275,7 +2294,7 @@ async def set_identity(
table.add_row(key, str(value) if value else "~")
console.print(table)
- return True
+ return True, ext_id
async def get_identity(
@@ -2433,6 +2452,7 @@ async def start_subnet(
)
if await response.is_success:
+ await print_extrinsic_id(response)
console.print(
f":white_heavy_check_mark: [green]Successfully started subnet {netuid}'s emission schedule.[/green]"
)
@@ -2467,7 +2487,9 @@ async def set_symbol(
if not await subtensor.subnet_exists(netuid):
err = f"Subnet {netuid} does not exist."
if json_output:
- json_console.print_json(data={"success": False, "message": err})
+ json_console.print_json(
+ data={"success": False, "message": err, "extrinsic_identifier": None}
+ )
else:
err_console.print(err)
return False
@@ -2503,16 +2525,26 @@ async def set_symbol(
wait_for_inclusion=True,
)
if await response.is_success:
+ ext_id = await response.get_extrinsic_identifier()
+ await print_extrinsic_id(response)
message = f"Successfully updated SN{netuid}'s symbol to {symbol}."
if json_output:
- json_console.print_json(data={"success": True, "message": message})
+ json_console.print_json(
+ data={
+ "success": True,
+ "message": message,
+ "extrinsic_identifier": ext_id,
+ }
+ )
else:
console.print(f":white_heavy_check_mark:[dark_sea_green3] {message}\n")
return True
else:
err = format_error_message(await response.error_message)
if json_output:
- json_console.print_json(data={"success": False, "message": err})
+ json_console.print_json(
+ data={"success": False, "message": err, "extrinsic_identifier": None}
+ )
else:
err_console.print(f":cross_mark: [red]Failed[/red]: {err}")
return False
diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py
index bb4bb43de..fe3601034 100644
--- a/bittensor_cli/src/commands/sudo.py
+++ b/bittensor_cli/src/commands/sudo.py
@@ -27,6 +27,7 @@
string_to_u16,
string_to_u64,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
if TYPE_CHECKING:
@@ -178,7 +179,7 @@ async def set_hyperparameter_extrinsic(
wait_for_inclusion: bool = False,
wait_for_finalization: bool = True,
prompt: bool = True,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
"""Sets a hyperparameter for a specific subnetwork.
:param subtensor: initialized SubtensorInterface object
@@ -191,8 +192,11 @@ async def set_hyperparameter_extrinsic(
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
or returns `False` if the extrinsic fails to be finalized within the timeout.
- :return: success: `True` if extrinsic was finalized or included in the block. If we did not wait for
+ :return: tuple including:
+ success: `True` if extrinsic was finalized or included in the block. If we did not wait for
finalization/inclusion, the response is `True`.
+ message: error message if the extrinsic failed
+ extrinsic_identifier: optional extrinsic identifier if the extrinsic was included
"""
print_verbose("Confirming subnet owner")
subnet_owner = await subtensor.query(
@@ -205,10 +209,10 @@ async def set_hyperparameter_extrinsic(
":cross_mark: [red]This wallet doesn't own the specified subnet.[/red]"
)
err_console.print(err_msg)
- return False, err_msg
+ return False, err_msg, None
if not (ulw := unlock_key(wallet)).success:
- return False, ulw.message
+ return False, ulw.message, None
arbitrary_extrinsic = False
@@ -227,7 +231,7 @@ async def set_hyperparameter_extrinsic(
if not Confirm.ask(
"This hyperparam is only settable by root sudo users. If you are not, this will fail. Please confirm"
):
- return False, "This hyperparam is only settable by root sudo users"
+ return False, "This hyperparam is only settable by root sudo users", None
substrate = subtensor.substrate
msg_value = value if not arbitrary_extrinsic else call_params
@@ -259,7 +263,7 @@ async def set_hyperparameter_extrinsic(
"Not enough values provided in the list for all parameters"
)
err_console.print(err_msg)
- return False, err_msg
+ return False, err_msg, None
call_params.update(
{name: val for name, val in zip(non_netuid_fields, value)}
@@ -287,25 +291,28 @@ async def set_hyperparameter_extrinsic(
)
else:
call = call_
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization
)
if not success:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
- return False, err_msg
- elif arbitrary_extrinsic:
- console.print(
- f":white_heavy_check_mark: "
- f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
- )
- return True, ""
- # Successful registration, final check for membership
+ return False, err_msg, None
else:
- console.print(
- f":white_heavy_check_mark: "
- f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
- )
- return True, ""
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
+ if arbitrary_extrinsic:
+ console.print(
+ f":white_heavy_check_mark: "
+ f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
+ )
+ return True, "", ext_id
+ # Successful registration, final check for membership
+ else:
+ console.print(
+ f":white_heavy_check_mark: "
+ f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
+ )
+ return True, "", ext_id
async def _get_senate_members(
@@ -506,7 +513,7 @@ async def vote_senate_extrinsic(
"approve": vote,
},
)
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization
)
if not success:
@@ -515,6 +522,7 @@ async def vote_senate_extrinsic(
return False
# Successful vote, final check for data
else:
+ await print_extrinsic_id(ext_receipt)
if vote_data := await subtensor.get_vote_data(proposal_hash):
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
if (
@@ -538,7 +546,7 @@ async def set_take_extrinsic(
wallet: Wallet,
delegate_ss58: str,
take: float = 0.0,
-) -> bool:
+) -> tuple[bool, Optional[str]]:
"""
Set delegate hotkey take
@@ -563,7 +571,7 @@ async def set_take_extrinsic(
if take_u16 == current_take_u16:
console.print("Nothing to do, take hasn't changed")
- return True
+ return True, None
if current_take_u16 < take_u16:
console.print(
@@ -581,7 +589,9 @@ async def set_take_extrinsic(
"take": take_u16,
},
)
- success, err = await subtensor.sign_and_send_extrinsic(call, wallet)
+ success, err, ext_receipt = await subtensor.sign_and_send_extrinsic(
+ call, wallet
+ )
else:
console.print(
@@ -599,15 +609,20 @@ async def set_take_extrinsic(
"take": take_u16,
},
)
- success, err = await subtensor.sign_and_send_extrinsic(call, wallet)
+ success, err, ext_receipt = await subtensor.sign_and_send_extrinsic(
+ call, wallet
+ )
if not success:
err_console.print(err)
+ ext_id = None
else:
console.print(
- ":white_heavy_check_mark: [dark_sea_green_3]Finalized[/dark_sea_green_3]"
+ ":white_heavy_check_mark: [dark_sea_green_3]Success[/dark_sea_green_3]"
)
- return success
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
+ return success, ext_id
# commands
@@ -621,7 +636,7 @@ async def sudo_set_hyperparameter(
param_value: Optional[str],
prompt: bool,
json_output: bool,
-) -> tuple[bool, str]:
+) -> tuple[bool, str, Optional[str]]:
"""Set subnet hyperparameters."""
is_allowed_value, value = allowed_value(param_name, param_value)
if not is_allowed_value:
@@ -630,17 +645,17 @@ async def sudo_set_hyperparameter(
f"Value is {param_value} but must be {value}"
)
err_console.print(err_msg)
- return False, err_msg
- success, err_msg = await set_hyperparameter_extrinsic(
+ return False, err_msg, None
+ success, err_msg, ext_id = await set_hyperparameter_extrinsic(
subtensor, wallet, netuid, param_name, value, prompt=prompt
)
if json_output:
- return success, err_msg
+ return success, err_msg, ext_id
if success:
console.print("\n")
print_verbose("Fetching hyperparameters")
await get_hyperparameters(subtensor, netuid=netuid)
- return success, err_msg
+ return success, err_msg, ext_id
async def get_hyperparameters(
@@ -907,13 +922,13 @@ async def display_current_take(subtensor: "SubtensorInterface", wallet: Wallet)
async def set_take(
wallet: Wallet, subtensor: "SubtensorInterface", take: float
-) -> bool:
+) -> tuple[bool, Optional[str]]:
"""Set delegate take."""
- async def _do_set_take() -> bool:
+ async def _do_set_take() -> tuple[bool, Optional[str]]:
if take > 0.18 or take < 0:
err_console.print("ERROR: Take value should not exceed 18% or be below 0%")
- return False
+ return False, None
block_hash = await subtensor.substrate.get_chain_head()
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
@@ -926,35 +941,34 @@ async def _do_set_take() -> bool:
f" any subnet. Please register using [{COLOR_PALETTE.G.SUBHEAD}]`btcli subnets register`"
f"[{COLOR_PALETTE.G.SUBHEAD}] and try again."
)
- return False
+ return False, None
- result: bool = await set_take_extrinsic(
+ result: tuple[bool, Optional[str]] = await set_take_extrinsic(
subtensor=subtensor,
wallet=wallet,
delegate_ss58=hotkey_ss58,
take=take,
)
+ success, ext_id = result
- if not result:
+ if not success:
err_console.print("Could not set the take")
- return False
+ return False, None
else:
new_take = await get_current_take(subtensor, wallet)
console.print(
f"New take is [{COLOR_PALETTE.P.RATE}]{new_take * 100.0:.2f}%"
)
- return True
+ return True, ext_id
console.print(
f"Setting take on [{COLOR_PALETTE.G.LINKS}]network: {subtensor.network}"
)
if not unlock_key(wallet, "hot").success and unlock_key(wallet, "cold").success:
- return False
-
- result_ = await _do_set_take()
+ return False, None
- return result_
+ return await _do_set_take()
async def trim(
@@ -993,20 +1007,30 @@ async def trim(
call_function="sudo_trim_to_max_allowed_uids",
call_params={"netuid": netuid, "max_n": max_n},
)
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call=call, wallet=wallet, era={"period": period}
)
if not success:
if json_output:
- json_console.print_json(data={"success": False, "message": err_msg})
+ json_console.print_json(
+ data={
+ "success": False,
+ "message": err_msg,
+ "extrinsic_identifier": None,
+ }
+ )
else:
err_console.print(f":cross_mark: [red]{err_msg}[/red]")
return False
else:
+ ext_id = await ext_receipt.get_extrinsic_identifier()
msg = f"Successfully trimmed UIDs on SN{netuid} to {max_n}"
if json_output:
- json_console.print_json(data={"success": True, "message": msg})
+ json_console.print_json(
+ data={"success": True, "message": msg, "extrinsic_identifier": ext_id}
+ )
else:
+ await print_extrinsic_id(ext_receipt)
console.print(
f":white_heavy_check_mark: [dark_sea_green3]{msg}[/dark_sea_green3]"
)
diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py
index 3b13a7fca..42fe8a0a6 100644
--- a/bittensor_cli/src/commands/wallets.py
+++ b/bittensor_cli/src/commands/wallets.py
@@ -49,6 +49,7 @@
blocks_to_duration,
decode_account_id,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
@@ -98,7 +99,7 @@ async def associate_hotkey(
)
with console.status(":satellite: Associating hotkey on-chain..."):
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call,
wallet,
wait_for_inclusion=True,
@@ -116,6 +117,7 @@ async def associate_hotkey(
f"wallet [blue]{wallet.name}[/blue], "
f"SS58: [{COLORS.GENERAL.CK}]{wallet.coldkeypub.ss58_address}[/{COLORS.GENERAL.CK}]"
)
+ await print_extrinsic_id(ext_receipt)
return True
@@ -1481,7 +1483,7 @@ async def transfer(
json_output: bool,
):
"""Transfer token of amount to destination."""
- result = await transfer_extrinsic(
+ result, ext_receipt = await transfer_extrinsic(
subtensor=subtensor,
wallet=wallet,
destination=destination,
@@ -1491,8 +1493,13 @@ async def transfer(
era=era,
prompt=prompt,
)
+ ext_id = (await ext_receipt.get_extrinsic_identifier()) if result else None
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id})
+ )
+ else:
+ await print_extrinsic_id(ext_receipt)
return result
@@ -1682,15 +1689,23 @@ async def swap_hotkey(
json_output: bool,
):
"""Swap your hotkey for all registered axons on the network."""
- result = await swap_hotkey_extrinsic(
+ result, ext_receipt = await swap_hotkey_extrinsic(
subtensor,
original_wallet,
new_wallet,
netuid=netuid,
prompt=prompt,
)
+ if result:
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ else:
+ ext_id = None
if json_output:
- json_console.print(json.dumps({"success": result}))
+ json_console.print(
+ json.dumps({"success": result, "extrinsic_identifier": ext_id})
+ )
+ else:
+ await print_extrinsic_id(ext_receipt)
return result
@@ -1731,7 +1746,7 @@ async def set_id(
github_repo: str,
prompt: bool,
json_output: bool = False,
-):
+) -> bool:
"""Create a new or update existing identity on-chain."""
output_dict = {"success": False, "identity": None, "error": ""}
identity_data = {
@@ -1756,16 +1771,20 @@ async def set_id(
with console.status(
" :satellite: [dark_sea_green3]Updating identity on-chain...", spinner="earth"
):
- success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
+ call, wallet
+ )
if not success:
err_console.print(f"[red]:cross_mark: Failed![/red] {err_msg}")
output_dict["error"] = err_msg
if json_output:
json_console.print(json.dumps(output_dict))
- return
+ return False
else:
console.print(":white_heavy_check_mark: [dark_sea_green3]Success!")
+ ext_id = await ext_receipt.get_extrinsic_identifier()
+ await print_extrinsic_id(ext_receipt)
output_dict["success"] = True
identity = await subtensor.query_identity(wallet.coldkeypub.ss58_address)
@@ -1774,9 +1793,12 @@ async def set_id(
for key, value in identity.items():
table.add_row(key, str(value) if value else "~")
output_dict["identity"] = identity
- console.print(table)
+ output_dict["extrinsic_identifier"] = ext_id
if json_output:
json_console.print(json.dumps(output_dict))
+ else:
+ console.print(table)
+ return True
async def get_id(
@@ -2016,9 +2038,9 @@ async def schedule_coldkey_swap(
},
),
)
-
+ swap_info = None
with console.status(":satellite: Scheduling coldkey swap on-chain..."):
- success, err_msg = await subtensor.sign_and_send_extrinsic(
+ success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call,
wallet,
wait_for_inclusion=True,
@@ -2033,13 +2055,29 @@ async def schedule_coldkey_swap(
console.print(
":white_heavy_check_mark: [green]Successfully scheduled coldkey swap"
)
+ await print_extrinsic_id(ext_receipt)
+ for event in await ext_receipt.triggered_events:
+ if (
+ event.get("event", {}).get("module_id") == "SubtensorModule"
+ and event.get("event", {}).get("event_id") == "ColdkeySwapScheduled"
+ ):
+ attributes = event["event"].get("attributes", {})
+ old_coldkey = decode_account_id(attributes["old_coldkey"][0])
- swap_info = await find_coldkey_swap_extrinsic(
- subtensor=subtensor,
- start_block=block_pre_call,
- end_block=block_post_call,
- wallet_ss58=wallet.coldkeypub.ss58_address,
- )
+ if old_coldkey == wallet.coldkeypub.ss58_address:
+ swap_info = {
+ "block_num": block_pre_call,
+ "dest_coldkey": decode_account_id(attributes["new_coldkey"][0]),
+ "execution_block": attributes["execution_block"],
+ }
+
+ if not swap_info:
+ swap_info = await find_coldkey_swap_extrinsic(
+ subtensor=subtensor,
+ start_block=block_pre_call,
+ end_block=block_post_call,
+ wallet_ss58=wallet.coldkeypub.ss58_address,
+ )
if not swap_info:
console.print(
diff --git a/bittensor_cli/src/commands/weights.py b/bittensor_cli/src/commands/weights.py
index 63e3b72f3..b61bbd81f 100644
--- a/bittensor_cli/src/commands/weights.py
+++ b/bittensor_cli/src/commands/weights.py
@@ -2,7 +2,7 @@
import json
import os
from datetime import datetime, timedelta
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Optional
from bittensor_wallet import Wallet
import numpy as np
@@ -16,6 +16,7 @@
format_error_message,
json_console,
get_hotkey_pub_ss58,
+ print_extrinsic_id,
)
from bittensor_cli.src.bittensor.extrinsics.root import (
convert_weights_and_uids_for_emit,
@@ -54,7 +55,7 @@ def __init__(
self.wait_for_inclusion = wait_for_inclusion
self.wait_for_finalization = wait_for_finalization
- async def set_weights_extrinsic(self) -> tuple[bool, str]:
+ async def set_weights_extrinsic(self) -> tuple[bool, str, Optional[str]]:
"""
Sets the inter-neuronal weights for the specified neuron. This process involves specifying the
influence or trust a neuron places on other neurons in the network, which is a fundamental aspect
@@ -80,7 +81,7 @@ async def set_weights_extrinsic(self) -> tuple[bool, str]:
f"Do you want to set weights:\n[bold white]"
f" weights: {formatted_weight_vals}\n uids: {weight_uids}[/bold white ]?"
):
- return False, "Prompt refused."
+ return False, "Prompt refused.", None
# Check if the commit-reveal mechanism is active for the given netuid.
if bool(
@@ -104,7 +105,7 @@ async def commit_weights(
self,
uids: list[int],
weights: list[int],
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, Optional[str], Optional[str]]:
"""
Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet.
This action serves as a commitment or snapshot of the neuron's current weight distribution.
@@ -121,12 +122,6 @@ async def commit_weights(
enhancing transparency and accountability within the Bittensor network.
"""
- # _logger.info(
- # "Committing weights with params: netuid={}, uids={}, weights={}, version_key={}".format(
- # netuid, uids, weights, version_key
- # )
- # )
-
# Generate the hash of the weights
commit_hash = generate_weight_hash(
address=get_hotkey_pub_ss58(self.wallet),
@@ -139,18 +134,21 @@ async def commit_weights(
# _logger.info("Commit Hash: {}".format(commit_hash))
try:
- success, message = await self.do_commit_weights(commit_hash=commit_hash)
+ success, message, ext_id = await self.do_commit_weights(
+ commit_hash=commit_hash
+ )
except SubstrateRequestException as e:
err_console.print(f"Error committing weights: {format_error_message(e)}")
# bittensor.logging.error(f"Error committing weights: {e}")
success = False
message = "No attempt made. Perhaps it is too soon to commit weights!"
+ ext_id = None
- return success, message
+ return success, message, ext_id
async def _commit_reveal(
self, weight_uids: list[int], weight_vals: list[int]
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, str, Optional[str]]:
interval = int(
await self.subtensor.get_hyperparameter(
param_name="get_commit_reveal_period",
@@ -165,7 +163,7 @@ async def _commit_reveal(
self.salt = list(os.urandom(salt_length))
# Attempt to commit the weights to the blockchain.
- commit_success, commit_msg = await self.commit_weights(
+ commit_success, commit_msg, ext_id = await self.commit_weights(
uids=weight_uids,
weights=weight_vals,
)
@@ -209,36 +207,42 @@ async def _commit_reveal(
console.print(f":cross_mark: [red]Failed[/red]: error:{commit_msg}")
# bittensor.logging.error(msg=commit_msg, prefix="Set weights with hash commit",
# suffix=f"Failed: {commit_msg}")
- return False, f"Failed to commit weights hash. {commit_msg}"
+ return False, f"Failed to commit weights hash. {commit_msg}", None
- async def reveal(self, weight_uids, weight_vals) -> tuple[bool, str]:
+ async def reveal(self, weight_uids, weight_vals) -> tuple[bool, str, Optional[str]]:
# Attempt to reveal the weights using the salt.
- success, msg = await self.reveal_weights_extrinsic(weight_uids, weight_vals)
+ success, msg, ext_id = await self.reveal_weights_extrinsic(
+ weight_uids, weight_vals
+ )
if success:
if not self.wait_for_finalization and not self.wait_for_inclusion:
- return True, "Not waiting for finalization or inclusion."
+ return True, "Not waiting for finalization or inclusion.", ext_id
console.print(
":white_heavy_check_mark: [green]Weights hash revealed on chain[/green]"
)
# bittensor.logging.success(prefix="Weights hash revealed", suffix=str(msg))
- return True, "Successfully revealed previously commited weights hash."
+ return (
+ True,
+ "Successfully revealed previously commited weights hash.",
+ ext_id,
+ )
else:
# bittensor.logging.error(
# msg=msg,
# prefix=f"Failed to reveal previously commited weights hash for salt: {salt}",
# suffix="Failed: ",
# )
- return False, "Failed to reveal weights."
+ return False, "Failed to reveal weights.", None
async def _set_weights_without_commit_reveal(
self,
weight_uids,
weight_vals,
- ) -> tuple[bool, str]:
- async def _do_set_weights():
+ ) -> tuple[bool, str, Optional[str]]:
+ async def _do_set_weights() -> tuple[bool, str, Optional[str]]:
call = await self.subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="set_weights",
@@ -262,37 +266,39 @@ async def _do_set_weights():
wait_for_finalization=self.wait_for_finalization,
)
except SubstrateRequestException as e:
- return False, format_error_message(e)
+ return False, format_error_message(e), None
# We only wait here if we expect finalization.
if not self.wait_for_finalization and not self.wait_for_inclusion:
- return True, "Not waiting for finalization or inclusion."
+ return True, "Not waiting for finalization or inclusion.", None
if await response.is_success:
- return True, "Successfully set weights."
+ ext_id_ = await response.get_extrinsic_identifier()
+ await print_extrinsic_id(response)
+ return True, "Successfully set weights.", ext_id_
else:
- return False, format_error_message(await response.error_message)
+ return False, format_error_message(await response.error_message), None
with console.status(
f":satellite: Setting weights on [white]{self.subtensor.network}[/white] ..."
):
- success, error_message = await _do_set_weights()
+ success, error_message, ext_id = await _do_set_weights()
if not self.wait_for_finalization and not self.wait_for_inclusion:
- return True, "Not waiting for finalization or inclusion."
+ return True, "Not waiting for finalization or inclusion.", None
if success:
console.print(":white_heavy_check_mark: [green]Finalized[/green]")
# bittensor.logging.success(prefix="Set weights", suffix="Finalized: " + str(success))
- return True, "Successfully set weights and finalized."
+ return True, "Successfully set weights and finalized.", ext_id
else:
# bittensor.logging.error(msg=error_message, prefix="Set weights", suffix="Failed: ")
- return False, error_message
+ return False, error_message, None
async def reveal_weights_extrinsic(
self, weight_uids, weight_vals
- ) -> tuple[bool, str]:
+ ) -> tuple[bool, str, Optional[str]]:
if self.prompt and not Confirm.ask("Would you like to reveal weights?"):
- return False, "User cancelled the operation."
+ return False, "User cancelled the operation.", None
call = await self.subtensor.substrate.compose_call(
call_module="SubtensorModule",
@@ -316,28 +322,36 @@ async def reveal_weights_extrinsic(
wait_for_finalization=self.wait_for_finalization,
)
except SubstrateRequestException as e:
- return False, format_error_message(e)
+ return False, format_error_message(e), None
if not self.wait_for_finalization and not self.wait_for_inclusion:
- success, error_message = True, ""
+ success, error_message, ext_id = True, "", None
else:
if await response.is_success:
- success, error_message = True, ""
+ success, error_message, ext_id = (
+ True,
+ "",
+ await response.get_extrinsic_identifier(),
+ )
+ await print_extrinsic_id(response)
else:
- success, error_message = (
+ success, error_message, ext_id = (
False,
format_error_message(await response.error_message),
+ None,
)
if success:
# bittensor.logging.info("Successfully revealed weights.")
- return True, "Successfully revealed weights."
+ return True, "Successfully revealed weights.", ext_id
else:
# bittensor.logging.error(f"Failed to reveal weights: {error_message}")
- return False, error_message
+ return False, error_message, ext_id
- async def do_commit_weights(self, commit_hash):
+ async def do_commit_weights(
+ self, commit_hash
+ ) -> tuple[bool, Optional[str], Optional[str]]:
call = await self.subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="commit_weights",
@@ -357,12 +371,14 @@ async def do_commit_weights(self, commit_hash):
)
if not self.wait_for_finalization and not self.wait_for_inclusion:
- return True, None
+ return True, None, None
if await response.is_success:
- return True, None
+ ext_id = await response.get_extrinsic_identifier()
+ await print_extrinsic_id(response)
+ return True, None, ext_id
else:
- return False, await response.error_message
+ return False, await response.error_message, None
# commands
@@ -399,9 +415,13 @@ async def reveal_weights(
extrinsic = SetWeightsExtrinsic(
subtensor, wallet, netuid, uids_, weights_, list(salt_), version, prompt=prompt
)
- success, message = await extrinsic.reveal(weight_uids, weight_vals)
+ success, message, ext_id = await extrinsic.reveal(weight_uids, weight_vals)
if json_output:
- json_console.print(json.dumps({"success": success, "message": message}))
+ json_console.print(
+ json.dumps(
+ {"success": success, "message": message, "extrinsic_identifier": ext_id}
+ )
+ )
else:
if success:
console.print("Weights revealed successfully")
@@ -436,9 +456,13 @@ async def commit_weights(
extrinsic = SetWeightsExtrinsic(
subtensor, wallet, netuid, uids_, weights_, list(salt_), version, prompt=prompt
)
- success, message = await extrinsic.set_weights_extrinsic()
+ success, message, ext_id = await extrinsic.set_weights_extrinsic()
if json_output:
- json_console.print(json.dumps({"success": success, "message": message}))
+ json_console.print(
+ json.dumps(
+ {"success": success, "message": message, "extrinsic_identifier": ext_id}
+ )
+ )
else:
if success:
console.print("Weights set successfully")
diff --git a/tests/e2e_tests/test_hyperparams_setting.py b/tests/e2e_tests/test_hyperparams_setting.py
index 916b00cac..24f83bdfe 100644
--- a/tests/e2e_tests/test_hyperparams_setting.py
+++ b/tests/e2e_tests/test_hyperparams_setting.py
@@ -62,7 +62,7 @@ def test_hyperparams_setting(local_chain, wallet_setup):
result_output = json.loads(result.stdout)
assert result_output["success"] is True
assert result_output["netuid"] == netuid
- print(result_output)
+ assert isinstance(result_output["extrinsic_identifier"], str)
# Fetch the hyperparameters of the subnet
hyperparams = exec_command_alice(
@@ -119,6 +119,7 @@ def test_hyperparams_setting(local_chain, wallet_setup):
)
cmd_json = json.loads(cmd.stdout)
assert cmd_json["success"] is True, (key, new_val, cmd.stdout, cmd_json)
+ assert isinstance(cmd_json["extrinsic_identifier"], str)
print(f"Successfully set hyperparameter {key} to value {new_val}")
# also test hidden hyperparam
cmd = exec_command_alice(
@@ -145,6 +146,7 @@ def test_hyperparams_setting(local_chain, wallet_setup):
)
cmd_json = json.loads(cmd.stdout)
assert cmd_json["success"] is True, (cmd.stdout, cmd_json)
+ assert isinstance(cmd_json["extrinsic_identifier"], str)
print("Successfully set hyperparameters")
print("Testing trimming UIDs")
cmd = exec_command_alice(
@@ -169,4 +171,5 @@ def test_hyperparams_setting(local_chain, wallet_setup):
)
cmd_json = json.loads(cmd.stdout)
assert cmd_json["success"] is True, (cmd.stdout, cmd_json)
+ assert isinstance(cmd_json["extrinsic_identifier"], str)
print("Successfully trimmed UIDs")
diff --git a/tests/e2e_tests/test_liquidity.py b/tests/e2e_tests/test_liquidity.py
index 218ef91f0..faaf6e05e 100644
--- a/tests/e2e_tests/test_liquidity.py
+++ b/tests/e2e_tests/test_liquidity.py
@@ -85,6 +85,7 @@ def liquidity_list():
result_output = json.loads(result.stdout)
assert result_output["success"] is True
assert result_output["netuid"] == netuid
+ assert isinstance(result_output["extrinsic_identifier"], str)
# verify no results for list thus far (subnet not yet started)
liquidity_list_result = liquidity_list()
@@ -115,6 +116,7 @@ def liquidity_list():
f"Successfully started subnet {netuid}'s emission schedule"
in start_subnet_emissions.stdout
), start_subnet_emissions.stderr
+ assert "Your extrinsic has been included " in start_subnet_emissions.stdout
liquidity_list_result = liquidity_list()
result_output = json.loads(liquidity_list_result.stdout)
@@ -146,6 +148,7 @@ def liquidity_list():
)
enable_user_liquidity_result = json.loads(enable_user_liquidity.stdout)
assert enable_user_liquidity_result["success"] is True
+ assert isinstance(enable_user_liquidity_result["extrinsic_identifier"], str)
add_liquidity = exec_command_alice(
command="liquidity",
@@ -174,6 +177,7 @@ def liquidity_list():
add_liquidity_result = json.loads(add_liquidity.stdout)
assert add_liquidity_result["success"] is True
assert add_liquidity_result["message"] == ""
+ assert isinstance(add_liquidity_result["extrinsic_identifier"], str)
liquidity_list_result = liquidity_list()
liquidity_list_result = json.loads(liquidity_list_result.stdout)
@@ -212,6 +216,7 @@ def liquidity_list():
)
modify_liquidity_result = json.loads(modify_liquidity.stdout)
assert modify_liquidity_result["success"] is True
+ assert isinstance(modify_liquidity_result["extrinsic_identifier"], str)
liquidity_list_result = json.loads(liquidity_list().stdout)
assert len(liquidity_list_result["positions"]) == 1
@@ -240,6 +245,9 @@ def liquidity_list():
)
removal_result = json.loads(removal.stdout)
assert removal_result[str(liquidity_position["id"])]["success"] is True
+ assert isinstance(
+ removal_result[str(liquidity_position["id"])]["extrinsic_identifier"], str
+ )
liquidity_list_result = json.loads(liquidity_list().stdout)
assert liquidity_list_result["success"] is True
diff --git a/tests/e2e_tests/test_senate.py b/tests/e2e_tests/test_senate.py
index 4cf12bb2b..c4cbebd7c 100644
--- a/tests/e2e_tests/test_senate.py
+++ b/tests/e2e_tests/test_senate.py
@@ -77,6 +77,9 @@ def test_senate(local_chain, wallet_setup):
],
)
assert "✅ Registered" in root_register.stdout, root_register.stderr
+ assert "Your extrinsic has been included " in root_register.stdout, (
+ root_register.stderr
+ )
# Fetch the senate members after registering to root
root_senate_after_reg = exec_command_bob(
@@ -156,6 +159,7 @@ def test_senate(local_chain, wallet_setup):
],
)
assert "✅ Vote cast" in vote_aye.stdout
+ assert "Your extrinsic has been included " in vote_aye.stdout
# Fetch proposals after voting aye
proposals_after_aye = exec_command_bob(
@@ -219,6 +223,7 @@ def test_senate(local_chain, wallet_setup):
],
)
assert "✅ Registered" in root_register.stdout
+ assert "Your extrinsic has been included " in root_register.stdout
# Vote on the proposal by Alice (vote nay)
vote_nay = exec_command_alice(
@@ -240,6 +245,7 @@ def test_senate(local_chain, wallet_setup):
],
)
assert "✅ Vote cast" in vote_nay.stdout
+ assert "Your extrinsic has been included " in vote_nay.stdout
# Fetch proposals after voting
proposals_after_nay = exec_command_bob(
diff --git a/tests/e2e_tests/test_staking_sudo.py b/tests/e2e_tests/test_staking_sudo.py
index 9034c51da..4096c87b0 100644
--- a/tests/e2e_tests/test_staking_sudo.py
+++ b/tests/e2e_tests/test_staking_sudo.py
@@ -1,6 +1,7 @@
import asyncio
import json
import re
+from typing import Union
from bittensor_cli.src.bittensor.balances import Balance
from .utils import turn_off_hyperparam_freeze_window
@@ -105,6 +106,7 @@ def test_staking(local_chain, wallet_setup):
result_output = json.loads(result.stdout)
assert result_output["success"] is True
assert result_output["netuid"] == netuid
+ assert isinstance(result_output["extrinsic_identifier"], str)
# Register another subnet with sudo as Alice
result_for_second_repo = exec_command_alice(
@@ -142,6 +144,7 @@ def test_staking(local_chain, wallet_setup):
result_output_second = json.loads(result_for_second_repo.stdout)
assert result_output_second["success"] is True
assert result_output_second["netuid"] == multiple_netuids[1]
+ assert isinstance(result_output_second["extrinsic_identifier"], str)
# Register Alice in netuid = 2 using her hotkey
register_subnet = exec_command_alice(
@@ -162,6 +165,7 @@ def test_staking(local_chain, wallet_setup):
],
)
assert "✅ Already Registered" in register_subnet.stdout
+ assert "Your extrinsic has been included" not in register_subnet.stdout
register_subnet_json = exec_command_alice(
command="subnets",
@@ -184,6 +188,7 @@ def test_staking(local_chain, wallet_setup):
register_subnet_json_output = json.loads(register_subnet_json.stdout)
assert register_subnet_json_output["success"] is True
assert register_subnet_json_output["msg"] == "Already registered"
+ assert register_subnet_json_output["extrinsic_identifier"] is None
# set identity
set_identity = exec_command_alice(
@@ -222,6 +227,7 @@ def test_staking(local_chain, wallet_setup):
)
set_identity_output = json.loads(set_identity.stdout)
assert set_identity_output["success"] is True
+ assert isinstance(set_identity_output["extrinsic_identifier"], str)
get_identity = exec_command_alice(
"subnets",
@@ -271,6 +277,7 @@ def test_staking(local_chain, wallet_setup):
set_symbol_output["message"]
== f"Successfully updated SN{netuid}'s symbol to シ."
)
+ assert isinstance(set_identity_output["extrinsic_identifier"], str)
get_s_price = exec_command_alice(
"subnets",
@@ -314,6 +321,9 @@ def test_staking(local_chain, wallet_setup):
f"Successfully started subnet {netuid_}'s emission schedule"
in start_subnet_emissions.stdout
), start_subnet_emissions.stderr
+ assert "Your extrinsic has been included" in start_subnet_emissions.stdout, (
+ start_subnet_emissions.stdout
+ )
# Add stake to Alice's hotkey
add_stake_single = exec_command_alice(
@@ -341,6 +351,9 @@ def test_staking(local_chain, wallet_setup):
],
)
assert "✅ Finalized" in add_stake_single.stdout, add_stake_single.stderr
+ assert "Your extrinsic has been included" in add_stake_single.stdout, (
+ add_stake_single.stdout
+ )
# Execute stake show for Alice's wallet
show_stake_adding_single = exec_command_alice(
@@ -408,6 +421,9 @@ def test_staking(local_chain, wallet_setup):
],
)
assert "✅ Finalized" in remove_stake.stdout
+ assert "Your extrinsic has been included" in remove_stake.stdout, (
+ remove_stake.stdout
+ )
add_stake_multiple = exec_command_alice(
command="stake",
@@ -436,18 +452,15 @@ def test_staking(local_chain, wallet_setup):
)
add_stake_multiple_output = json.loads(add_stake_multiple.stdout)
for netuid_ in multiple_netuids:
- assert (
- add_stake_multiple_output["staking_success"][str(netuid_)][
- wallet_alice.hotkey.ss58_address
- ]
- is True
- )
- assert (
- add_stake_multiple_output["error_messages"][str(netuid_)][
+
+ def line(key: str) -> Union[str, bool]:
+ return add_stake_multiple_output[key][str(netuid_)][
wallet_alice.hotkey.ss58_address
]
- == ""
- )
+
+ assert line("staking_success") is True
+ assert line("error_messages") == ""
+ assert isinstance(line("extrinsic_ids"), str)
# Fetch the hyperparameters of the subnet
hyperparams = exec_command_alice(
@@ -507,6 +520,9 @@ def test_staking(local_chain, wallet_setup):
assert (
"✅ Hyperparameter max_burn changed to 10000000000" in change_hyperparams.stdout
)
+ assert "Your extrinsic has been included" in change_hyperparams.stdout, (
+ change_hyperparams.stdout
+ )
# Fetch the hyperparameters again to verify
updated_hyperparams = exec_command_alice(
@@ -576,6 +592,7 @@ def test_staking(local_chain, wallet_setup):
assert change_yuma3_hyperparam_json["success"] is True, (
change_yuma3_hyperparam.stdout
)
+ assert isinstance(change_yuma3_hyperparam_json["extrinsic_identifier"], str)
changed_yuma3_hyperparam = exec_command_alice(
command="sudo",
@@ -626,3 +643,4 @@ def test_staking(local_chain, wallet_setup):
change_arbitrary_hyperparam.stdout,
change_arbitrary_hyperparam.stderr,
)
+ assert isinstance(change_yuma3_hyperparam_json["extrinsic_identifier"], str)
diff --git a/tests/e2e_tests/test_unstaking.py b/tests/e2e_tests/test_unstaking.py
index 4b7ca0765..d4e3eef76 100644
--- a/tests/e2e_tests/test_unstaking.py
+++ b/tests/e2e_tests/test_unstaking.py
@@ -89,6 +89,7 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Registered subnetwork with netuid: 2" in result.stdout
+ assert "Your extrinsic has been included" in result.stdout, result.stdout
# Create second subnet (netuid = 3)
result = exec_command_alice(
@@ -123,6 +124,7 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Registered subnetwork with netuid: 3" in result.stdout
+ assert "Your extrinsic has been included" in result.stdout, result.stdout
# Start emission schedule for subnets
start_call_netuid_0 = exec_command_alice(
@@ -144,6 +146,9 @@ def test_unstaking(local_chain, wallet_setup):
"Successfully started subnet 0's emission schedule."
in start_call_netuid_0.stdout
)
+ assert "Your extrinsic has been included" in start_call_netuid_0.stdout, (
+ start_call_netuid_0.stdout
+ )
start_call_netuid_2 = exec_command_alice(
command="subnets",
sub_command="start",
@@ -163,6 +168,7 @@ def test_unstaking(local_chain, wallet_setup):
"Successfully started subnet 2's emission schedule."
in start_call_netuid_2.stdout
)
+ assert "Your extrinsic has been included" in start_call_netuid_2.stdout
start_call_netuid_3 = exec_command_alice(
command="subnets",
@@ -183,6 +189,7 @@ def test_unstaking(local_chain, wallet_setup):
"Successfully started subnet 3's emission schedule."
in start_call_netuid_3.stdout
)
+ assert "Your extrinsic has been included" in start_call_netuid_3.stdout
# Register Bob in one subnet
register_result = exec_command_bob(
command="subnets",
@@ -204,6 +211,9 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Registered" in register_result.stdout, register_result.stderr
+ assert "Your extrinsic has been included" in register_result.stdout, (
+ register_result.stdout
+ )
# Add stake to subnets
for netuid in [0, 2, 3]:
@@ -232,6 +242,9 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Finalized" in stake_result.stdout, stake_result.stderr
+ assert "Your extrinsic has been included" in stake_result.stdout, (
+ stake_result.stdout
+ )
stake_list = exec_command_bob(
command="stake",
@@ -279,6 +292,9 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Finalized" in partial_unstake_netuid_2.stdout
+ assert "Your extrinsic has been included" in partial_unstake_netuid_2.stdout, (
+ partial_unstake_netuid_2.stdout
+ )
# Verify partial unstake
stake_list = exec_command_bob(
@@ -348,6 +364,9 @@ def test_unstaking(local_chain, wallet_setup):
assert (
"✅ Finalized: Successfully unstaked all Alpha stakes" in unstake_alpha.stdout
)
+ assert "Your extrinsic has been included" in unstake_alpha.stdout, (
+ unstake_alpha.stdout
+ )
# Add stake again to subnets
for netuid in [0, 2, 3]:
@@ -376,6 +395,7 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Finalized" in stake_result.stdout
+ assert "Your extrinsic has been included" in stake_result.stdout
# Remove all stakes
unstake_all = exec_command_bob(
@@ -397,4 +417,5 @@ def test_unstaking(local_chain, wallet_setup):
],
)
assert "✅ Finalized: Successfully unstaked all stakes from" in unstake_all.stdout
+ assert "Your extrinsic has been included" in unstake_all.stdout, unstake_all.stdout
print("Passed unstaking tests 🎉")
diff --git a/tests/e2e_tests/test_wallet_interactions.py b/tests/e2e_tests/test_wallet_interactions.py
index e6a4bb22d..9dd3ea4f1 100644
--- a/tests/e2e_tests/test_wallet_interactions.py
+++ b/tests/e2e_tests/test_wallet_interactions.py
@@ -443,17 +443,19 @@ def test_wallet_identities(local_chain, wallet_setup):
assert "✅ Success!" in set_id.stdout
set_id_output = set_id.stdout.splitlines()
- assert alice_identity["name"] in set_id_output[6]
- assert alice_identity["url"] in set_id_output[7]
- assert alice_identity["github_repo"] in set_id_output[8]
- assert alice_identity["image"] in set_id_output[9]
- assert alice_identity["discord"] in set_id_output[10]
- assert alice_identity["description"] in set_id_output[11]
- assert alice_identity["additional"] in set_id_output[12]
+ assert "Your extrinsic has been included as" in set_id_output[1]
+
+ assert alice_identity["name"] in set_id_output[7]
+ assert alice_identity["url"] in set_id_output[8]
+ assert alice_identity["github_repo"] in set_id_output[9]
+ assert alice_identity["image"] in set_id_output[10]
+ assert alice_identity["discord"] in set_id_output[11]
+ assert alice_identity["description"] in set_id_output[12]
+ assert alice_identity["additional"] in set_id_output[13]
# TODO: Currently coldkey + hotkey are the same for test wallets.
# Maybe we can add a new key to help in distinguishing
- assert wallet_alice.coldkeypub.ss58_address in set_id_output[5]
+ assert wallet_alice.coldkeypub.ss58_address in set_id_output[6]
# Execute btcli get-identity using hotkey
get_identity = exec_command_alice(