Skip to content
43 changes: 0 additions & 43 deletions bittensor_cli/src/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from enum import Enum
from dataclasses import dataclass
from typing import Any, Optional


class Constants:
Expand Down Expand Up @@ -37,47 +35,6 @@ class Constants:
delegates_detail_url = "https://raw.githubusercontent.com/opentensor/bittensor-delegates/main/public/delegates.json"


@dataclass
class DelegatesDetails:
display: str
additional: list[tuple[str, str]]
web: str
legal: Optional[str] = None
riot: Optional[str] = None
email: Optional[str] = None
pgp_fingerprint: Optional[str] = None
image: Optional[str] = None
twitter: Optional[str] = None

@classmethod
def from_chain_data(cls, data: dict[str, Any]) -> "DelegatesDetails":
def decode(key: str, default=""):
try:
if isinstance(data.get(key), dict):
value = next(data.get(key).values())
return bytes(value[0]).decode("utf-8")
elif isinstance(data.get(key), int):
return data.get(key)
elif isinstance(data.get(key), tuple):
return bytes(data.get(key)[0]).decode("utf-8")
else:
return default
except (UnicodeDecodeError, TypeError):
return default

return cls(
display=decode("display"),
additional=decode("additional", []),
web=decode("web"),
legal=decode("legal"),
riot=decode("riot"),
email=decode("email"),
pgp_fingerprint=decode("pgp_fingerprint", None),
image=decode("image"),
twitter=decode("twitter"),
)


class Defaults:
netuid = 1
rate_tolerance = 0.005
Expand Down
35 changes: 0 additions & 35 deletions bittensor_cli/src/bittensor/subtensor_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@
CrowdloanData,
ColdkeySwapAnnouncementInfo,
)
from bittensor_cli.src import DelegatesDetails
from bittensor_cli.src.bittensor.balances import Balance, fixed_to_float
from bittensor_cli.src import Constants, defaults, TYPE_REGISTRY
from bittensor_cli.src.bittensor.extrinsics.mev_shield import encrypt_extrinsic
from bittensor_cli.src.bittensor.utils import (
format_error_message,
console,
print_error,
decode_hex_identity_dict,
validate_chain_endpoint,
u16_normalized_float,
MEV_SHIELD_PUBLIC_KEY_SIZE,
Expand Down Expand Up @@ -1518,39 +1516,6 @@ async def get_vote_data(
else:
return ProposalVoteData(vote_data)

async def get_delegate_identities(
self, block_hash: Optional[str] = None
) -> dict[str, DelegatesDetails]:
"""
Fetches delegates identities from the chain and GitHub. Preference is given to chain data, and missing info
is filled-in by the info from GitHub. At some point, we want to totally move away from fetching this info
from GitHub, but chain data is still limited in that regard.

:param block_hash: the hash of the blockchain block for the query

:return: {ss58: DelegatesDetails, ...}

"""
identities_info = await self.substrate.query_map(
module="Registry",
storage_function="IdentityOf",
block_hash=block_hash,
)

all_delegates_details = {}
async for ss58_address, identity in identities_info:
all_delegates_details.update(
{
decode_account_id(
ss58_address[0]
): DelegatesDetails.from_chain_data(
decode_hex_identity_dict(identity.value["info"])
)
}
)

return all_delegates_details

async def get_mechagraph_info(
self, netuid: int, mech_id: int, block_hash: Optional[str] = None
) -> Optional[MetagraphInfo]:
Expand Down
18 changes: 18 additions & 0 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ def create_table(*columns, title: str = "", **overrides) -> Table:
return Table(*columns, **config)


def get_hotkey_identity_name(
identities: dict[str, Any], hotkey_ss58: str
) -> Optional[str]:
"""Return a hotkey display name from the V2 identity map, if present."""
hotkey_identity = identities.get("hotkeys", {}).get(hotkey_ss58, {})
identity_data = hotkey_identity.get("identity", {})
return identity_data.get("name") or identity_data.get("display") or None


def get_coldkey_identity_name(
identities: dict[str, Any], coldkey_ss58: str
) -> Optional[str]:
"""Return a coldkey display name from the V2 identity map, if present."""
coldkey_identity = identities.get("coldkeys", {}).get(coldkey_ss58, {})
identity_data = coldkey_identity.get("identity", {})
return identity_data.get("name") or identity_data.get("display") or None


jinja_env = Environment(
loader=PackageLoader("bittensor_cli", "src/bittensor/templates"),
autoescape=select_autoescape(),
Expand Down
36 changes: 6 additions & 30 deletions bittensor_cli/src/commands/stake/auto_staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
print_error,
unlock_key,
print_extrinsic_id,
get_hotkey_identity_name,
get_coldkey_identity_name,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -45,7 +47,6 @@ async def show_auto_stake_destinations(
subnet_info,
auto_destinations,
identities,
delegate_identities,
) = await asyncio.gather(
subtensor.all_subnets(block_hash=chain_head),
subtensor.get_auto_stake_destinations(
Expand All @@ -54,36 +55,21 @@ async def show_auto_stake_destinations(
reuse_block=True,
),
subtensor.fetch_coldkey_hotkey_identities(block_hash=chain_head),
subtensor.get_delegate_identities(block_hash=chain_head),
)

subnet_map = {info.netuid: info for info in subnet_info}
auto_destinations = auto_destinations or {}
identities = identities or {}
delegate_identities = delegate_identities or {}
hotkey_identities = identities.get("hotkeys", {})

def resolve_identity(hotkey: str) -> Optional[str]:
if not hotkey:
return None

identity_entry = hotkey_identities.get(hotkey, {}).get("identity")
if identity_entry:
display_name = identity_entry.get("name") or identity_entry.get("display")
if display_name:
return display_name

delegate_info = delegate_identities.get(hotkey)
if delegate_info and getattr(delegate_info, "display", ""):
return delegate_info.display

return None
return get_hotkey_identity_name(identities, hotkey)

coldkey_display = wallet_name
if not coldkey_display:
coldkey_identity = identities.get("coldkeys", {}).get(coldkey_ss58, {})
if identity_data := coldkey_identity.get("identity"):
coldkey_display = identity_data.get("name") or identity_data.get("display")
coldkey_display = get_coldkey_identity_name(identities, coldkey_ss58)
if not coldkey_display:
coldkey_display = f"{coldkey_ss58[:6]}...{coldkey_ss58[-6:]}"

Expand Down Expand Up @@ -183,28 +169,18 @@ async def set_auto_stake_destination(

try:
chain_head = await subtensor.substrate.get_chain_head()
subnet_info, identities, delegate_identities = await asyncio.gather(
subnet_info, identities = await asyncio.gather(
subtensor.subnet(netuid, block_hash=chain_head),
subtensor.fetch_coldkey_hotkey_identities(block_hash=chain_head),
subtensor.get_delegate_identities(block_hash=chain_head),
)
except ValueError:
print_error(f"Subnet with netuid {netuid} does not exist")
return False

hotkey_identity = ""
identities = identities or {}
delegate_identities = delegate_identities or {}

hotkey_identity_entry = identities.get("hotkeys", {}).get(hotkey_ss58, {})
if identity_data := hotkey_identity_entry.get("identity"):
hotkey_identity = (
identity_data.get("name") or identity_data.get("display") or ""
)
if not hotkey_identity:
delegate_info = delegate_identities.get(hotkey_ss58)
if delegate_info and getattr(delegate_info, "display", ""):
hotkey_identity = delegate_info.display
hotkey_identity = get_hotkey_identity_name(identities, hotkey_ss58) or ""

if prompt_user and not json_output:
table = create_table(
Expand Down
34 changes: 14 additions & 20 deletions bittensor_cli/src/commands/stake/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
millify_tao,
get_subnet_name,
json_console,
get_hotkey_identity_name,
)

if TYPE_CHECKING:
Expand All @@ -40,16 +41,17 @@ async def stake_list(
async def get_stake_data(block_hash_: str = None):
(
sub_stakes_,
registered_delegate_info_,
hotkey_identity_map_,
_dynamic_info,
) = await asyncio.gather(
subtensor.get_stake_for_coldkey(
coldkey_ss58=coldkey_address, block_hash=block_hash_
),
subtensor.get_delegate_identities(block_hash=block_hash_),
subtensor.fetch_coldkey_hotkey_identities(block_hash=block_hash_),
subtensor.all_subnets(block_hash=block_hash_),
)

hotkey_identity_map_ = hotkey_identity_map_ or {"hotkeys": {}, "coldkeys": {}}
claimable_amounts_ = {}
if sub_stakes_:
claimable_amounts_ = await subtensor.get_claimable_stakes_for_coldkey(
Expand All @@ -61,11 +63,15 @@ async def get_stake_data(block_hash_: str = None):
dynamic_info__ = {info.netuid: info for info in _dynamic_info}
return (
sub_stakes_,
registered_delegate_info_,
hotkey_identity_map_,
dynamic_info__,
claimable_amounts_,
)

def format_hotkey_name(hotkey_ss58_: str, hotkey_identity_map_: dict) -> str:
display_name = get_hotkey_identity_name(hotkey_identity_map_, hotkey_ss58_)
return f"{display_name} ({hotkey_ss58_})" if display_name else hotkey_ss58_

def define_table(
hotkey_name_: str,
rows: list[list[str]],
Expand Down Expand Up @@ -156,11 +162,7 @@ def create_table(
substakes_: list[StakeInfo],
claimable_amounts_: dict[str, dict[int, Balance]],
):
name_ = (
f"{registered_delegate_info[hotkey_].display} ({hotkey_})"
if hotkey_ in registered_delegate_info
else hotkey_
)
name_ = format_hotkey_name(hotkey_, hotkey_identity_map)
rows = []
total_tao_value_ = Balance(0)
total_swapped_tao_value_ = Balance(0)
Expand Down Expand Up @@ -481,7 +483,7 @@ def format_cell(
(
(
sub_stakes,
registered_delegate_info,
hotkey_identity_map,
dynamic_info,
claimable_amounts,
),
Expand Down Expand Up @@ -509,11 +511,7 @@ def format_cell(
"\n[bold]Multiple hotkeys found. Please select one for live monitoring:[/bold]"
)
for idx, hotkey in enumerate(hotkeys_to_substakes.keys()):
name = (
f"{registered_delegate_info[hotkey].display} ({hotkey})"
if hotkey in registered_delegate_info
else hotkey
)
name = format_hotkey_name(hotkey, hotkey_identity_map)
console.print(f"[{idx}] [{COLOR_PALETTE['GENERAL']['HEADER']}]{name}")

selected_idx = Prompt.ask(
Expand All @@ -524,11 +522,7 @@ def format_cell(
else:
selected_hotkey = list(hotkeys_to_substakes.keys())[0]

hotkey_name = (
f"{registered_delegate_info[selected_hotkey].display} ({selected_hotkey})"
if selected_hotkey in registered_delegate_info
else selected_hotkey
)
hotkey_name = format_hotkey_name(selected_hotkey, hotkey_identity_map)

refresh_interval = 10 # seconds
progress = Progress(
Expand All @@ -549,7 +543,7 @@ def format_cell(
block_hash = await subtensor.substrate.get_chain_head()
(
sub_stakes,
registered_delegate_info,
_hotkey_identity_map,
dynamic_info_,
claimable_amounts_live,
) = await get_stake_data(block_hash)
Expand Down
13 changes: 3 additions & 10 deletions bittensor_cli/src/commands/stake/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
unlock_key,
get_hotkey_pub_ss58,
print_extrinsic_id,
get_hotkey_identity_name,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -324,10 +325,9 @@ async def stake_move_transfer_selection(
wallet: Wallet,
):
"""Selection interface for moving stakes between hotkeys and subnets."""
stakes, ck_hk_identities, old_identities = await asyncio.gather(
stakes, ck_hk_identities = await asyncio.gather(
subtensor.get_stake_for_coldkey(coldkey_ss58=wallet.coldkeypub.ss58_address),
subtensor.fetch_coldkey_hotkey_identities(),
subtensor.get_delegate_identities(),
)

hotkey_stakes = {}
Expand All @@ -353,14 +353,7 @@ async def stake_move_transfer_selection(

hotkeys_info = []
for idx, (hotkey_ss58, netuid_stakes) in enumerate(hotkey_stakes.items()):
if hk_identity := ck_hk_identities["hotkeys"].get(hotkey_ss58):
hotkey_name = hk_identity.get("identity", {}).get(
"name", ""
) or hk_identity.get("display", "~")
elif old_identity := old_identities.get(hotkey_ss58):
hotkey_name = old_identity.display
else:
hotkey_name = "~"
hotkey_name = get_hotkey_identity_name(ck_hk_identities, hotkey_ss58) or "~"
hotkeys_info.append(
{
"index": idx,
Expand Down
Loading
Loading