Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
MEV_SHIELD_PUBLIC_KEY_SIZE = 1184

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

Expand Down
127 changes: 123 additions & 4 deletions bittensor_cli/src/commands/subnets/subnets.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
print_extrinsic_id,
check_img_mimetype,
)
from async_substrate_interface.types import Runtime

if TYPE_CHECKING:
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
Expand All @@ -53,6 +54,86 @@
# helpers and extrinsics


async def get_subtensor_constant(
subtensor: "SubtensorInterface",
constant_name: str,
runtime: Optional[Runtime] = None,
block_hash: Optional[str] = None,
) -> int:
"""
Get a constant from the SubtensorModule.

Args:
subtensor: SubtensorInterface object for chain interaction
constant_name: Name of the constant to get
runtime: Runtime object (optional)
block_hash: Block hash (optional)

Returns:
The value of the constant in RAO
"""
runtime = runtime or await subtensor.substrate.init_runtime(block_hash=block_hash)

result = await subtensor.substrate.get_constant(
module_name="SubtensorModule",
constant_name=constant_name,
block_hash=block_hash,
runtime=runtime,
)
return getattr(result, "value", result)


async def get_identity_deposit(
subtensor: "SubtensorInterface",
block_hash: Optional[str] = None,
) -> Balance:
"""
Get the deposit required for subnet identity operations.

Args:
subtensor: SubtensorInterface object for chain interaction
block_hash: Block hash (optional)

Returns:
The identity deposit amount as a Balance object
"""
try:
# Try common constant names for identity deposit
constant_names = [
"SubnetIdentityDeposit",
"IdentityDeposit",
"SubnetIdentityFee",
"IdentityFee",
"SubnetIdentityDepositAmount",
"IdentityDepositAmount",
]
Comment on lines +102 to +109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these come from?


runtime = await subtensor.substrate.init_runtime(block_hash=block_hash)

for constant_name in constant_names:
try:
deposit_raw = await get_subtensor_constant(
subtensor, constant_name, runtime=runtime, block_hash=block_hash
)
print_verbose(
f"Found identity deposit constant: {constant_name} = {deposit_raw} RAO"
)
return Balance.from_rao(deposit_raw)
except Exception as e:
print_verbose(f"Constant {constant_name} not found: {e}")
continue

# If no constant found, return 0 and log a warning
print_verbose(
f"Warning: Could not find identity deposit constant. "
f"Tried: {', '.join(constant_names)}"
)
return Balance.from_rao(0)
except Exception as e:
print_verbose(f"Error retrieving identity deposit: {e}")
return Balance.from_rao(0)


def format_claim_type_for_root(claim_info: dict, total_subnets: int) -> str:
"""
Format claim type for root network metagraph.
Expand Down Expand Up @@ -120,6 +201,7 @@ async def register_subnetwork_extrinsic(
wait_for_finalization: bool = True,
prompt: bool = False,
mev_protection: bool = True,
json_output: bool = False,
) -> tuple[bool, Optional[int], Optional[str]]:
"""Registers a new subnetwork.

Expand Down Expand Up @@ -192,6 +274,9 @@ async def _find_event_attributes_in_extrinsic_receipt(

has_identity = any(subnet_identity.values())
if has_identity:
# Query identity deposit amount when creating subnet with identity
identity_deposit = await get_identity_deposit(subtensor)

identity_data = {
"subnet_name": subnet_identity["subnet_name"].encode()
if subnet_identity.get("subnet_name")
Expand Down Expand Up @@ -229,6 +314,27 @@ async def _find_event_attributes_in_extrinsic_receipt(
)
return False, None, None

# Display identity deposit information (only if not in JSON output mode)
if not json_output:
if identity_deposit.rao > 0:
console.print(
f"Identity deposit required: [{COLOR_PALETTE['POOLS']['TAO']}]{identity_deposit}[/{COLOR_PALETTE['POOLS']['TAO']}]"
)
# Check if user has enough balance for identity deposit
if identity_deposit > your_balance:
err_console.print(
f"Your balance of: [{COLOR_PALETTE.POOLS.TAO}]{your_balance}[/{COLOR_PALETTE.POOLS.TAO}] "
f"is not enough to cover the identity deposit of "
f"[{COLOR_PALETTE.POOLS.TAO}]{identity_deposit}[/{COLOR_PALETTE.POOLS.TAO}]."
)
return False, None, None
else:
# Deposit is 0 or unknown - still inform user that identity creation may have a fee
console.print(
f"[yellow]Note:[/yellow] Creating subnet with identity may require a deposit. "
f"Deposit amount could not be determined from chain constants."
)

if not unlock_key(wallet).success:
return False, None, None

Expand Down Expand Up @@ -1599,7 +1705,8 @@ async def show_subnet(
"uids": json_out_rows,
}
if json_output:
json_console.print(json.dumps(output_dict))
# Use print() directly for JSON output to avoid Rich Console adding ANSI codes
print(json.dumps(output_dict))

mech_line = (
f"\n Mechanism ID: [{COLOR_PALETTE['GENERAL']['SUBHEADING_EXTRA_1']}]#{selected_mechanism_id}"
Expand Down Expand Up @@ -1738,11 +1845,13 @@ async def create(
prompt=prompt,
proxy=proxy,
mev_protection=mev_protection,
json_output=json_output,
)
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(
# Use print() directly for JSON output to avoid Rich Console adding ANSI codes
print(
json.dumps(
{"success": success, "netuid": netuid, "extrinsic_identifier": ext_id}
)
Expand Down Expand Up @@ -2519,9 +2628,18 @@ async def set_identity(
if not unlock_key(wallet).success:
return False, None

# Query identity deposit amount
identity_deposit = await get_identity_deposit(subtensor)

if prompt:
deposit_msg = ""
if identity_deposit.rao > 0:
deposit_msg = f" This requires a deposit of [{COLOR_PALETTE['POOLS']['TAO']}]{identity_deposit}[/{COLOR_PALETTE['POOLS']['TAO']}]."
else:
deposit_msg = " This is subject to a fee."

if not Confirm.ask(
"Are you sure you want to set subnet's identity? This is subject to a fee."
f"Are you sure you want to set subnet's identity?{deposit_msg}"
):
return False, None

Expand Down Expand Up @@ -2618,7 +2736,8 @@ async def get_identity(
table.add_row(key, str(value) if value else "~")
dict_out[key] = value
if json_output:
json_console.print(json.dumps(dict_out))
# Use print() directly for JSON output to avoid Rich Console adding ANSI codes
print(json.dumps(dict_out))
else:
console.print(table)
return identity
Expand Down
19 changes: 19 additions & 0 deletions tests/e2e_tests/test_set_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def test_set_id(local_chain, wallet_setup):
"--no-mev-protection",
],
)
print("=======================================")
print(result.stdout)
print("=======================================")
result_output = json.loads(result.stdout)
assert result_output["success"] is True

Expand Down Expand Up @@ -107,10 +110,18 @@ def test_set_id(local_chain, wallet_setup):
],
inputs=["Y", "Y"],
)

assert (
f"Are you sure you want to use {sn_logo_url} as your image URL?"
in set_identity.stdout
)
# Verify that deposit information is shown in the prompt
assert "Are you sure you want to set subnet's identity?" in set_identity.stdout
# Check for either deposit amount or "subject to a fee" message
assert (
"deposit" in set_identity.stdout.lower()
or "subject to a fee" in set_identity.stdout.lower()
)
get_identity = exec_command_alice(
"subnets",
"get-identity",
Expand Down Expand Up @@ -181,6 +192,13 @@ def test_set_id(local_chain, wallet_setup):
f"Are you sure you want to use {sn_logo_url} as your image URL?"
not in set_identity.stdout
)
# Verify that deposit information is shown in the prompt
assert "Are you sure you want to set subnet's identity?" in set_identity.stdout
# Check for either deposit amount or "subject to a fee" message
assert (
"deposit" in set_identity.stdout.lower()
or "subject to a fee" in set_identity.stdout.lower()
)
get_identity = exec_command_alice(
"subnets",
"get-identity",
Expand All @@ -192,6 +210,7 @@ def test_set_id(local_chain, wallet_setup):
"--json-output",
],
)

get_identity_output = json.loads(get_identity.stdout)
assert get_identity_output["subnet_name"] == sn_name
assert get_identity_output["github_repo"] == sn_github
Expand Down
Loading