From 6ea32ba45fad71481320ef8d1a4c0dd311989592 Mon Sep 17 00:00:00 2001 From: DanielDerefaka Date: Mon, 22 Dec 2025 03:51:22 +0100 Subject: [PATCH 1/4] feat: Add standardized JSON output utilities (#635) This PR introduces a centralized json_utils module to standardize JSON output across all btcli commands, addressing the inconsistencies noted in issue #635. Changes: - Add `bittensor_cli/src/bittensor/json_utils.py` with standardized helpers: - `json_response()` - Base response formatter - `json_success()` - Success response helper - `json_error()` - Error response helper - `json_transaction()` - Transaction response helper - `serialize_balance()` - Consistent Balance serialization - Update `wallets.py` with sample usage (3 functions updated as POC) - Add comprehensive unit tests (21 tests) for schema compliance Standard Response Format: ```json { "success": bool, "data": {...}, // Optional: command-specific data "error": str // Optional: error message } ``` This establishes the foundation for full migration of all commands to use consistent JSON schemas. Additional commands can be migrated incrementally. Closes #635 --- bittensor_cli/src/bittensor/json_utils.py | 174 +++++++++++++++ bittensor_cli/src/commands/wallets.py | 91 +++----- tests/unit_tests/test_json_utils.py | 258 ++++++++++++++++++++++ 3 files changed, 463 insertions(+), 60 deletions(-) create mode 100644 bittensor_cli/src/bittensor/json_utils.py create mode 100644 tests/unit_tests/test_json_utils.py diff --git a/bittensor_cli/src/bittensor/json_utils.py b/bittensor_cli/src/bittensor/json_utils.py new file mode 100644 index 000000000..a58dd18dc --- /dev/null +++ b/bittensor_cli/src/bittensor/json_utils.py @@ -0,0 +1,174 @@ +""" +Standardized JSON output utilities for btcli. + +This module provides consistent JSON response formatting across all btcli commands. +All JSON outputs should use these utilities to ensure schema compliance. + +Standard Response Format: +{ + "success": bool, # Required: Whether the operation succeeded + "data": {...}, # Optional: Command-specific response data + "error": str # Optional: Error message if success=False +} + +For transaction responses, data should include: +{ + "extrinsic_hash": str, # The transaction hash + "block_hash": str # The block containing the transaction +} +""" + +import json +from typing import Any, Optional, Union +from rich.console import Console + +# JSON console for outputting JSON responses +json_console = Console() + + +def json_response( + success: bool, + data: Optional[Any] = None, + error: Optional[str] = None, +) -> str: + """ + Create a standardized JSON response string. + + Args: + success: Whether the operation succeeded + data: Optional response data (dict, list, or primitive) + error: Optional error message (typically used when success=False) + + Returns: + JSON string with standardized format + + Examples: + >>> json_response(True, {"balance": 100.5}) + '{"success": true, "data": {"balance": 100.5}}' + + >>> json_response(False, error="Wallet not found") + '{"success": false, "error": "Wallet not found"}' + """ + response: dict[str, Any] = {"success": success} + + if data is not None: + response["data"] = data + + if error is not None: + response["error"] = error + + return json.dumps(response) + + +def json_success(data: Any) -> str: + """ + Create a successful JSON response. + + Args: + data: Response data to include + + Returns: + JSON string with success=True and the provided data + """ + return json_response(success=True, data=data) + + +def json_error(error: str, data: Optional[Any] = None) -> str: + """ + Create an error JSON response. + + Args: + error: Error message describing what went wrong + data: Optional additional context data + + Returns: + JSON string with success=False and error message + """ + return json_response(success=False, data=data, error=error) + + +def json_transaction( + success: bool, + extrinsic_hash: Optional[str] = None, + block_hash: Optional[str] = None, + error: Optional[str] = None, + **extra_data: Any, +) -> str: + """ + Create a standardized transaction response. + + Args: + success: Whether the transaction succeeded + extrinsic_hash: The transaction/extrinsic hash + block_hash: The block hash containing the transaction + error: Error message if transaction failed + **extra_data: Additional transaction-specific data + + Returns: + JSON string with transaction details + """ + data: dict[str, Any] = {} + + if extrinsic_hash is not None: + data["extrinsic_hash"] = extrinsic_hash + + if block_hash is not None: + data["block_hash"] = block_hash + + # Add any extra data + data.update(extra_data) + + return json_response(success=success, data=data if data else None, error=error) + + +def print_json(response: str) -> None: + """ + Print a JSON response to the console. + + Args: + response: JSON string to print + """ + json_console.print(response) + + +def print_json_success(data: Any) -> None: + """ + Print a successful JSON response. + + Args: + data: Response data to include + """ + print_json(json_success(data)) + + +def print_json_error(error: str, data: Optional[Any] = None) -> None: + """ + Print an error JSON response. + + Args: + error: Error message + data: Optional additional context + """ + print_json(json_error(error, data)) + + +def serialize_balance(balance: Any) -> dict[str, Union[int, float]]: + """ + Serialize a Balance object to a consistent dictionary format. + + Args: + balance: A Balance object or numeric value + + Returns: + Dictionary with 'rao' (int) and 'tao' (float) keys + """ + if hasattr(balance, "rao") and hasattr(balance, "tao"): + return {"rao": int(balance.rao), "tao": float(balance.tao)} + elif isinstance(balance, (int, float)): + # Assume it's already in tao if float, rao if int + if isinstance(balance, float): + return {"rao": int(balance * 1e9), "tao": balance} + else: + return {"rao": balance, "tao": balance / 1e9} + else: + return {"rao": 0, "tao": 0.0} diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 96c812be5..3f8e90f3f 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -54,6 +54,11 @@ get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import ( + json_success, + json_error, + print_json, +) class SortByBalance(Enum): @@ -184,33 +189,21 @@ async def regen_coldkey( f"coldkey ss58: ({new_wallet.coldkeypub.ss58_address})", ) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "data": { - "name": new_wallet.name, - "path": new_wallet.path, - "hotkey": new_wallet.hotkey_str, - "hotkey_ss58": get_hotkey_pub_ss58(new_wallet), - "coldkey_ss58": new_wallet.coldkeypub.ss58_address, - }, - "error": "", - } - ) - ) + print_json(json_success({ + "name": new_wallet.name, + "path": new_wallet.path, + "hotkey": new_wallet.hotkey_str, + "hotkey_ss58": get_hotkey_pub_ss58(new_wallet), + "coldkey_ss58": new_wallet.coldkeypub.ss58_address, + })) except ValueError: print_error("Mnemonic phrase is invalid") if json_output: - json_console.print( - '{"success": false, "error": "Mnemonic phrase is invalid", "data": null}' - ) + print_json(json_error("Mnemonic phrase is invalid")) except KeyFileError: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( - '{"success": false, "error": "Keyfile is not writable", "data": null}' - ) + print_json(json_error("Keyfile is not writable")) async def regen_coldkey_pub( @@ -234,27 +227,17 @@ async def regen_coldkey_pub( f"coldkey ss58: ({new_coldkeypub.coldkeypub.ss58_address})", ) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "data": { - "name": new_coldkeypub.name, - "path": new_coldkeypub.path, - "hotkey": new_coldkeypub.hotkey_str, - "hotkey_ss58": get_hotkey_pub_ss58(new_coldkeypub), - "coldkey_ss58": new_coldkeypub.coldkeypub.ss58_address, - }, - "error": "", - } - ) - ) + print_json(json_success({ + "name": new_coldkeypub.name, + "path": new_coldkeypub.path, + "hotkey": new_coldkeypub.hotkey_str, + "hotkey_ss58": get_hotkey_pub_ss58(new_coldkeypub), + "coldkey_ss58": new_coldkeypub.coldkeypub.ss58_address, + })) except KeyFileError: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( - '{"success": false, "error": "Keyfile is not writable", "data": null}' - ) + print_json(json_error("Keyfile is not writable")) async def regen_hotkey( @@ -291,33 +274,21 @@ async def regen_hotkey( f"hotkey ss58: ({new_hotkey_.hotkeypub.ss58_address})", ) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "data": { - "name": new_hotkey_.name, - "path": new_hotkey_.path, - "hotkey": new_hotkey_.hotkey_str, - "hotkey_ss58": new_hotkey_.hotkeypub.ss58_address, - "coldkey_ss58": new_hotkey_.coldkeypub.ss58_address, - }, - "error": "", - } - ) - ) + print_json(json_success({ + "name": new_hotkey_.name, + "path": new_hotkey_.path, + "hotkey": new_hotkey_.hotkey_str, + "hotkey_ss58": new_hotkey_.hotkeypub.ss58_address, + "coldkey_ss58": new_hotkey_.coldkeypub.ss58_address, + })) except ValueError: print_error("Mnemonic phrase is invalid") if json_output: - json_console.print( - '{"success": false, "error": "Mnemonic phrase is invalid", "data": null}' - ) + print_json(json_error("Mnemonic phrase is invalid")) except KeyFileError: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( - '{"success": false, "error": "Keyfile is not writable", "data": null}' - ) + print_json(json_error("Keyfile is not writable")) async def regen_hotkey_pub( diff --git a/tests/unit_tests/test_json_utils.py b/tests/unit_tests/test_json_utils.py new file mode 100644 index 000000000..216244c79 --- /dev/null +++ b/tests/unit_tests/test_json_utils.py @@ -0,0 +1,258 @@ +""" +Unit tests for JSON output utilities. + +Tests the standardized JSON response formatting used across btcli commands. +""" + +import json +import pytest +from io import StringIO +from unittest.mock import patch + +from bittensor_cli.src.bittensor.json_utils import ( + json_response, + json_success, + json_error, + json_transaction, + serialize_balance, +) + + +class TestJsonResponse: + """Tests for the json_response function.""" + + def test_success_with_data(self): + """Test successful response with data.""" + result = json_response(success=True, data={"key": "value"}) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"] == {"key": "value"} + assert "error" not in parsed + + def test_success_without_data(self): + """Test successful response without data.""" + result = json_response(success=True) + parsed = json.loads(result) + + assert parsed["success"] is True + assert "data" not in parsed + assert "error" not in parsed + + def test_error_response(self): + """Test error response.""" + result = json_response(success=False, error="Something went wrong") + parsed = json.loads(result) + + assert parsed["success"] is False + assert parsed["error"] == "Something went wrong" + + def test_error_with_data(self): + """Test error response with additional data.""" + result = json_response( + success=False, + data={"partial": "data"}, + error="Partial failure" + ) + parsed = json.loads(result) + + assert parsed["success"] is False + assert parsed["data"] == {"partial": "data"} + assert parsed["error"] == "Partial failure" + + def test_nested_data(self): + """Test response with nested data structures.""" + data = { + "wallet": { + "name": "test", + "hotkeys": ["hk1", "hk2"], + "balance": {"rao": 1000000000, "tao": 1.0} + } + } + result = json_response(success=True, data=data) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"]["wallet"]["name"] == "test" + assert len(parsed["data"]["wallet"]["hotkeys"]) == 2 + + +class TestJsonSuccess: + """Tests for the json_success helper.""" + + def test_simple_data(self): + """Test success with simple data.""" + result = json_success({"status": "ok"}) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"]["status"] == "ok" + + def test_list_data(self): + """Test success with list data.""" + result = json_success([1, 2, 3]) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"] == [1, 2, 3] + + def test_primitive_data(self): + """Test success with primitive data.""" + result = json_success("simple string") + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"] == "simple string" + + +class TestJsonError: + """Tests for the json_error helper.""" + + def test_simple_error(self): + """Test simple error message.""" + result = json_error("Connection failed") + parsed = json.loads(result) + + assert parsed["success"] is False + assert parsed["error"] == "Connection failed" + assert "data" not in parsed + + def test_error_with_context(self): + """Test error with additional context data.""" + result = json_error("Validation failed", data={"field": "amount"}) + parsed = json.loads(result) + + assert parsed["success"] is False + assert parsed["error"] == "Validation failed" + assert parsed["data"]["field"] == "amount" + + +class TestJsonTransaction: + """Tests for the json_transaction helper.""" + + def test_successful_transaction(self): + """Test successful transaction response.""" + result = json_transaction( + success=True, + extrinsic_hash="0x123abc", + block_hash="0x456def" + ) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"]["extrinsic_hash"] == "0x123abc" + assert parsed["data"]["block_hash"] == "0x456def" + + def test_failed_transaction(self): + """Test failed transaction response.""" + result = json_transaction( + success=False, + error="Insufficient balance" + ) + parsed = json.loads(result) + + assert parsed["success"] is False + assert parsed["error"] == "Insufficient balance" + + def test_transaction_with_extra_data(self): + """Test transaction with extra data fields.""" + result = json_transaction( + success=True, + extrinsic_hash="0x123", + amount=100.5, + recipient="5xyz..." + ) + parsed = json.loads(result) + + assert parsed["success"] is True + assert parsed["data"]["extrinsic_hash"] == "0x123" + assert parsed["data"]["amount"] == 100.5 + assert parsed["data"]["recipient"] == "5xyz..." + + +class TestSerializeBalance: + """Tests for balance serialization.""" + + def test_balance_object(self): + """Test serializing a Balance-like object.""" + class MockBalance: + rao = 1000000000 + tao = 1.0 + + result = serialize_balance(MockBalance()) + + assert result["rao"] == 1000000000 + assert result["tao"] == 1.0 + + def test_float_value(self): + """Test serializing a float (assumes TAO).""" + result = serialize_balance(2.5) + + assert result["tao"] == 2.5 + assert result["rao"] == 2500000000 + + def test_int_value(self): + """Test serializing an int (assumes RAO).""" + result = serialize_balance(5000000000) + + assert result["rao"] == 5000000000 + assert result["tao"] == 5.0 + + def test_unknown_type(self): + """Test serializing unknown type returns zeros.""" + result = serialize_balance("invalid") + + assert result["rao"] == 0 + assert result["tao"] == 0.0 + + +class TestJsonOutputConsistency: + """Tests to verify JSON output schema consistency.""" + + def test_success_always_has_success_field(self): + """Verify success responses always include 'success' field.""" + responses = [ + json_success({}), + json_success([]), + json_success("test"), + json_success(None), + ] + + for response in responses: + parsed = json.loads(response) + assert "success" in parsed + assert parsed["success"] is True + + def test_error_always_has_success_and_error_fields(self): + """Verify error responses always include required fields.""" + responses = [ + json_error("error1"), + json_error("error2", data={}), + ] + + for response in responses: + parsed = json.loads(response) + assert "success" in parsed + assert "error" in parsed + assert parsed["success"] is False + + def test_no_null_data_in_success(self): + """Verify success responses don't include null data.""" + result = json_success({"key": "value"}) + parsed = json.loads(result) + + # Data should be present, not null + assert parsed.get("data") is not None + + def test_json_is_valid(self): + """Verify all outputs are valid JSON.""" + test_cases = [ + json_response(True, {"test": "data"}), + json_success({"nested": {"deep": True}}), + json_error("test error"), + json_transaction(True, "0x123", "0x456"), + ] + + for output in test_cases: + # Should not raise + json.loads(output) From 10b292d639afc9a6b752463eff24452a418bbadd Mon Sep 17 00:00:00 2001 From: DanielDerefaka Date: Mon, 22 Dec 2025 16:31:22 +0100 Subject: [PATCH 2/4] feat: Expand JSON utils with TransactionResult classes and extrinsic_identifier - Add TransactionResult and MultiTransactionResult classes for consistent transaction responses across all commands - Add print_transaction_response() helper function - Update schema to use {success, message, extrinsic_identifier} format - Migrate wallets.py: transfer, swap_hotkey, set_id - Migrate sudo.py: trim command - Migrate stake/add.py and stake/remove.py - Migrate liquidity.py: add_liquidity, remove_liquidity, modify_liquidity - Update tests for new transaction response utilities (25 tests passing) This addresses feedback from @thewhaleking on PR #781 to apply standardized JSON output to all json_console usages with extrinsic_identifier support. Closes #635 --- bittensor_cli/src/bittensor/json_utils.py | 171 +++++++++++++----- .../src/commands/liquidity/liquidity.py | 39 ++-- bittensor_cli/src/commands/stake/add.py | 13 +- bittensor_cli/src/commands/stake/remove.py | 5 +- bittensor_cli/src/commands/sudo.py | 15 +- bittensor_cli/src/commands/wallets.py | 19 +- tests/unit_tests/test_json_utils.py | 123 +++++++++---- 7 files changed, 251 insertions(+), 134 deletions(-) diff --git a/bittensor_cli/src/bittensor/json_utils.py b/bittensor_cli/src/bittensor/json_utils.py index a58dd18dc..e094fa88b 100644 --- a/bittensor_cli/src/bittensor/json_utils.py +++ b/bittensor_cli/src/bittensor/json_utils.py @@ -4,17 +4,18 @@ This module provides consistent JSON response formatting across all btcli commands. All JSON outputs should use these utilities to ensure schema compliance. -Standard Response Format: +Standard Transaction Response Format: { - "success": bool, # Required: Whether the operation succeeded - "data": {...}, # Optional: Command-specific response data - "error": str # Optional: Error message if success=False + "success": bool, # Required: Whether the operation succeeded + "message": str | None, # Optional: Human-readable message + "extrinsic_identifier": str | None # Optional: Block-extrinsic ID (e.g., "12345-2") } -For transaction responses, data should include: +Standard Data Response Format: { - "extrinsic_hash": str, # The transaction hash - "block_hash": str # The block containing the transaction + "success": bool, # Required: Whether the operation succeeded + "data": {...}, # Optional: Command-specific response data + "error": str # Optional: Error message if success=False } """ @@ -22,17 +23,119 @@ from typing import Any, Optional, Union from rich.console import Console -# JSON console for outputting JSON responses json_console = Console() +def transaction_response( + success: bool, + message: Optional[str] = None, + extrinsic_identifier: Optional[str] = None, +) -> dict[str, Any]: + """ + Create a standardized transaction response dictionary. + + Args: + success: Whether the transaction succeeded + message: Human-readable status message + extrinsic_identifier: The extrinsic ID (e.g., "12345678-2") + + Returns: + Dictionary with standardized transaction format + """ + return { + "success": success, + "message": message, + "extrinsic_identifier": extrinsic_identifier, + } + + +def print_transaction_response( + success: bool, + message: Optional[str] = None, + extrinsic_identifier: Optional[str] = None, +) -> None: + """ + Print a standardized transaction response as JSON. + + Args: + success: Whether the transaction succeeded + message: Human-readable status message + extrinsic_identifier: The extrinsic ID (e.g., "12345678-2") + """ + json_console.print_json(data=transaction_response(success, message, extrinsic_identifier)) + + +class TransactionResult: + """ + Helper class for building transaction responses. + + Provides a clean interface for transaction commands that need to + build up response data before printing. + """ + + def __init__( + self, + success: bool, + message: Optional[str] = None, + extrinsic_identifier: Optional[str] = None, + ): + self.success = success + self.message = message + self.extrinsic_identifier = extrinsic_identifier + + def as_dict(self) -> dict[str, Any]: + """Return the response as a dictionary.""" + return transaction_response( + self.success, + self.message, + self.extrinsic_identifier, + ) + + def print(self) -> None: + """Print the response as JSON.""" + json_console.print_json(data=self.as_dict()) + + +class MultiTransactionResult: + """ + Helper class for commands that process multiple transactions. + + Builds a keyed dictionary of transaction results. + """ + + def __init__(self): + self._results: dict[str, TransactionResult] = {} + + def add( + self, + key: str, + success: bool, + message: Optional[str] = None, + extrinsic_identifier: Optional[str] = None, + ) -> None: + """Add a transaction result with the given key.""" + self._results[key] = TransactionResult(success, message, extrinsic_identifier) + + def add_result(self, key: str, result: TransactionResult) -> None: + """Add an existing TransactionResult with the given key.""" + self._results[key] = result + + def as_dict(self) -> dict[str, dict[str, Any]]: + """Return all results as a dictionary.""" + return {k: v.as_dict() for k, v in self._results.items()} + + def print(self) -> None: + """Print all results as JSON.""" + json_console.print_json(data=self.as_dict()) + + def json_response( success: bool, data: Optional[Any] = None, error: Optional[str] = None, ) -> str: """ - Create a standardized JSON response string. + Create a standardized JSON response string for data queries. Args: success: Whether the operation succeeded @@ -62,7 +165,7 @@ def json_response( def json_success(data: Any) -> str: """ - Create a successful JSON response. + Create a successful JSON response string. Args: data: Response data to include @@ -75,7 +178,7 @@ def json_success(data: Any) -> str: def json_error(error: str, data: Optional[Any] = None) -> str: """ - Create an error JSON response. + Create an error JSON response string. Args: error: Error message describing what went wrong @@ -87,43 +190,9 @@ def json_error(error: str, data: Optional[Any] = None) -> str: return json_response(success=False, data=data, error=error) -def json_transaction( - success: bool, - extrinsic_hash: Optional[str] = None, - block_hash: Optional[str] = None, - error: Optional[str] = None, - **extra_data: Any, -) -> str: - """ - Create a standardized transaction response. - - Args: - success: Whether the transaction succeeded - extrinsic_hash: The transaction/extrinsic hash - block_hash: The block hash containing the transaction - error: Error message if transaction failed - **extra_data: Additional transaction-specific data - - Returns: - JSON string with transaction details - """ - data: dict[str, Any] = {} - - if extrinsic_hash is not None: - data["extrinsic_hash"] = extrinsic_hash - - if block_hash is not None: - data["block_hash"] = block_hash - - # Add any extra data - data.update(extra_data) - - return json_response(success=success, data=data if data else None, error=error) - - def print_json(response: str) -> None: """ - Print a JSON response to the console. + Print a JSON string response to the console. Args: response: JSON string to print @@ -152,6 +221,16 @@ def print_json_error(error: str, data: Optional[Any] = None) -> None: print_json(json_error(error, data)) +def print_json_data(data: Any) -> None: + """ + Print data directly as JSON (for simple data responses). + + Args: + data: Data to print as JSON + """ + json_console.print_json(data=data) + + def serialize_balance(balance: Any) -> dict[str, Union[int, float]]: """ Serialize a Balance object to a consistent dictionary format. diff --git a/bittensor_cli/src/commands/liquidity/liquidity.py b/bittensor_cli/src/commands/liquidity/liquidity.py index 7997afc5f..6ad87ef13 100644 --- a/bittensor_cli/src/commands/liquidity/liquidity.py +++ b/bittensor_cli/src/commands/liquidity/liquidity.py @@ -14,6 +14,12 @@ json_console, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import ( + print_transaction_response, + print_json_data, + TransactionResult, + MultiTransactionResult, +) from bittensor_cli.src.bittensor.balances import Balance, fixed_to_float from bittensor_cli.src.commands.liquidity.utils import ( LiquidityPosition, @@ -295,13 +301,7 @@ async def add_liquidity( else: ext_id = None if json_output: - json_console.print_json( - data={ - "success": success, - "message": message, - "extrinsic_identifier": ext_id, - } - ) + print_transaction_response(success, message, ext_id) else: if success: console.print( @@ -558,9 +558,7 @@ async def show_liquidity_list( if not json_output: console.print(liquidity_table) else: - json_console.print( - json.dumps({"success": True, "err_msg": "", "positions": json_table}) - ) + print_json_data({"success": True, "err_msg": "", "positions": json_table}) async def remove_liquidity( @@ -584,9 +582,7 @@ async def remove_liquidity( success, msg, positions = await get_liquidity_list(subtensor, wallet, netuid) if not success: if json_output: - json_console.print_json( - data={"success": False, "err_msg": msg, "positions": positions} - ) + print_json_data({"success": False, "err_msg": msg, "positions": positions}) else: return print_error(f"Error: {msg}") return None @@ -629,14 +625,11 @@ async def remove_liquidity( else: print_error(f"Error removing {posid}: {msg}") else: - json_table = {} + json_results = MultiTransactionResult() 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) + ext_id = await ext_receipt.get_extrinsic_identifier() if ext_receipt else None + json_results.add(str(posid), success, msg, ext_id) + json_results.print() return None @@ -657,7 +650,7 @@ async def modify_liquidity( if not await subtensor.subnet_exists(netuid=netuid): err_msg = f"Subnet with netuid: {netuid} does not exist in {subtensor}." if json_output: - json_console.print(json.dumps({"success": False, "err_msg": err_msg})) + print_transaction_response(False, err_msg, None) else: print_error(err_msg) return False @@ -687,9 +680,7 @@ async def modify_liquidity( ) if json_output: 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} - ) + print_transaction_response(success, msg, ext_id) else: if success: await print_extrinsic_id(ext_receipt) diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index 18c6578eb..f967d08f9 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -26,6 +26,7 @@ get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data from bittensor_wallet import Wallet if TYPE_CHECKING: @@ -528,13 +529,11 @@ async def stake_extrinsic( staking_address ] = await ext_receipt.get_extrinsic_identifier() if json_output: - json_console.print_json( - data={ - "staking_success": successes, - "error_messages": error_messages, - "extrinsic_ids": extrinsic_ids, - } - ) + print_json_data({ + "staking_success": successes, + "error_messages": error_messages, + "extrinsic_ids": extrinsic_ids, + }) # Helper functions diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index 1cc7116a5..90c8de22f 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -29,6 +29,7 @@ get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -370,7 +371,7 @@ async def unstake( f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]Unstaking operations completed." ) if json_output: - json_console.print_json(data=successes) + print_json_data(successes) return True @@ -580,7 +581,7 @@ async def unstake_all( "extrinsic_identifier": ext_id, } if json_output: - json_console.print(json.dumps({"success": successes})) + print_json_data({"success": successes}) # Extrinsics diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index b039ea4f2..24240d8e1 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -33,6 +33,7 @@ get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_transaction_response if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import ( @@ -1252,7 +1253,7 @@ async def trim( if subnet_owner != wallet.coldkeypub.ss58_address: err_msg = "This wallet doesn't own the specified subnet." if json_output: - json_console.print_json(data={"success": False, "message": err_msg}) + print_transaction_response(False, err_msg, None) else: print_error(err_msg) return False @@ -1274,13 +1275,7 @@ async def trim( ) if not success: if json_output: - json_console.print_json( - data={ - "success": False, - "message": err_msg, - "extrinsic_identifier": None, - } - ) + print_transaction_response(False, err_msg, None) else: print_error(err_msg) return False @@ -1288,9 +1283,7 @@ async def trim( 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, "extrinsic_identifier": ext_id} - ) + print_transaction_response(True, msg, ext_id) else: await print_extrinsic_id(ext_receipt) console.print( diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 3f8e90f3f..596c0d927 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -58,6 +58,8 @@ json_success, json_error, print_json, + print_json_data, + print_transaction_response, ) @@ -1525,9 +1527,8 @@ async def transfer( ) ext_id = (await ext_receipt.get_extrinsic_identifier()) if result else None if json_output: - json_console.print( - json.dumps({"success": result, "extrinsic_identifier": ext_id}) - ) + msg = "Transfer successful" if result else "Transfer failed" + print_transaction_response(result, msg, ext_id) else: await print_extrinsic_id(ext_receipt) return result @@ -1733,9 +1734,8 @@ async def swap_hotkey( else: ext_id = None if json_output: - json_console.print( - json.dumps({"success": result, "extrinsic_identifier": ext_id}) - ) + msg = "Hotkey swap successful" if result else "Hotkey swap failed" + print_transaction_response(result, msg, ext_id) else: await print_extrinsic_id(ext_receipt) return result @@ -1811,23 +1811,20 @@ async def set_id( print_error(f"Failed! {err_msg}") output_dict["error"] = err_msg if json_output: - json_console.print(json.dumps(output_dict)) + print_transaction_response(False, err_msg, None) 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) table = create_identity_table(title="New on-chain Identity") table.add_row("Address", wallet.coldkeypub.ss58_address) for key, value in identity.items(): table.add_row(key, str(value) if value else "~") - output_dict["identity"] = identity - output_dict["extrinsic_identifier"] = ext_id if json_output: - json_console.print(json.dumps(output_dict)) + print_transaction_response(True, "Identity set successfully", ext_id) else: console.print(table) return True diff --git a/tests/unit_tests/test_json_utils.py b/tests/unit_tests/test_json_utils.py index 216244c79..277ef69a8 100644 --- a/tests/unit_tests/test_json_utils.py +++ b/tests/unit_tests/test_json_utils.py @@ -13,8 +13,10 @@ json_response, json_success, json_error, - json_transaction, serialize_balance, + transaction_response, + TransactionResult, + MultiTransactionResult, ) @@ -127,47 +129,100 @@ def test_error_with_context(self): assert parsed["data"]["field"] == "amount" -class TestJsonTransaction: - """Tests for the json_transaction helper.""" +class TestTransactionResponse: + """Tests for the transaction_response helper.""" def test_successful_transaction(self): """Test successful transaction response.""" - result = json_transaction( + result = transaction_response( success=True, - extrinsic_hash="0x123abc", - block_hash="0x456def" + message="Transfer successful", + extrinsic_identifier="12345678-2" ) - parsed = json.loads(result) - assert parsed["success"] is True - assert parsed["data"]["extrinsic_hash"] == "0x123abc" - assert parsed["data"]["block_hash"] == "0x456def" + assert result["success"] is True + assert result["message"] == "Transfer successful" + assert result["extrinsic_identifier"] == "12345678-2" def test_failed_transaction(self): """Test failed transaction response.""" - result = json_transaction( + result = transaction_response( success=False, - error="Insufficient balance" + message="Insufficient balance" ) - parsed = json.loads(result) - assert parsed["success"] is False - assert parsed["error"] == "Insufficient balance" + assert result["success"] is False + assert result["message"] == "Insufficient balance" + assert result["extrinsic_identifier"] is None - def test_transaction_with_extra_data(self): - """Test transaction with extra data fields.""" - result = json_transaction( + def test_transaction_without_message(self): + """Test transaction without message.""" + result = transaction_response( success=True, - extrinsic_hash="0x123", - amount=100.5, - recipient="5xyz..." + extrinsic_identifier="12345678-3" ) - parsed = json.loads(result) - assert parsed["success"] is True - assert parsed["data"]["extrinsic_hash"] == "0x123" - assert parsed["data"]["amount"] == 100.5 - assert parsed["data"]["recipient"] == "5xyz..." + assert result["success"] is True + assert result["message"] is None + assert result["extrinsic_identifier"] == "12345678-3" + + +class TestTransactionResult: + """Tests for the TransactionResult class.""" + + def test_as_dict(self): + """Test conversion to dictionary.""" + result = TransactionResult( + success=True, + message="Success", + extrinsic_identifier="12345-1" + ) + d = result.as_dict() + + assert d["success"] is True + assert d["message"] == "Success" + assert d["extrinsic_identifier"] == "12345-1" + + def test_failed_result(self): + """Test failed transaction result.""" + result = TransactionResult( + success=False, + message="Error occurred" + ) + d = result.as_dict() + + assert d["success"] is False + assert d["message"] == "Error occurred" + assert d["extrinsic_identifier"] is None + + +class TestMultiTransactionResult: + """Tests for the MultiTransactionResult class.""" + + def test_add_results(self): + """Test adding multiple results.""" + multi = MultiTransactionResult() + multi.add("pos1", True, "Success 1", "12345-1") + multi.add("pos2", False, "Failed 2", None) + + d = multi.as_dict() + + assert "pos1" in d + assert "pos2" in d + assert d["pos1"]["success"] is True + assert d["pos1"]["extrinsic_identifier"] == "12345-1" + assert d["pos2"]["success"] is False + assert d["pos2"]["extrinsic_identifier"] is None + + def test_add_result_object(self): + """Test adding TransactionResult objects.""" + multi = MultiTransactionResult() + result = TransactionResult(True, "Test", "123-1") + multi.add_result("key1", result) + + d = multi.as_dict() + assert d["key1"]["success"] is True + assert d["key1"]["message"] == "Test" class TestSerializeBalance: @@ -236,13 +291,16 @@ def test_error_always_has_success_and_error_fields(self): assert "error" in parsed assert parsed["success"] is False - def test_no_null_data_in_success(self): - """Verify success responses don't include null data.""" - result = json_success({"key": "value"}) - parsed = json.loads(result) + def test_transaction_response_schema(self): + """Verify transaction responses have consistent schema.""" + success_result = transaction_response(True, "OK", "123-1") + fail_result = transaction_response(False, "Failed", None) - # Data should be present, not null - assert parsed.get("data") is not None + # Both should have all three keys + for result in [success_result, fail_result]: + assert "success" in result + assert "message" in result + assert "extrinsic_identifier" in result def test_json_is_valid(self): """Verify all outputs are valid JSON.""" @@ -250,7 +308,6 @@ def test_json_is_valid(self): json_response(True, {"test": "data"}), json_success({"nested": {"deep": True}}), json_error("test error"), - json_transaction(True, "0x123", "0x456"), ] for output in test_cases: From 8f19ac4082ac95149053911bdaedcb77c3b244cc Mon Sep 17 00:00:00 2001 From: DanielDerefaka Date: Mon, 29 Dec 2025 23:55:45 +0100 Subject: [PATCH 3/4] Migrate all json_console usages to standardized utilities Complete migration of 169 json_console usages across 22 files to use the standardized print_json_data, print_transaction_response, and print_transaction_with_data utilities from json_utils.py. Files migrated: - crowd/*.py (6 files) - subnets/*.py (3 files) - stake/*.py (6 files) - wallets.py - sudo.py - proxy.py - liquidity/liquidity.py - axon/axon.py - weights.py - json_utils.py (added print_transaction_with_data helper) --- bittensor_cli/src/bittensor/json_utils.py | 24 ++++ bittensor_cli/src/commands/axon/axon.py | 43 +++--- .../src/commands/crowd/contribute.py | 35 +++-- bittensor_cli/src/commands/crowd/create.py | 35 +++-- bittensor_cli/src/commands/crowd/dissolve.py | 19 ++- bittensor_cli/src/commands/crowd/refund.py | 17 ++- bittensor_cli/src/commands/crowd/update.py | 31 +++-- bittensor_cli/src/commands/crowd/view.py | 11 +- .../src/commands/liquidity/liquidity.py | 6 +- bittensor_cli/src/commands/proxy.py | 123 +++++------------- bittensor_cli/src/commands/stake/add.py | 1 - .../src/commands/stake/auto_staking.py | 20 ++- .../src/commands/stake/children_hotkeys.py | 6 +- bittensor_cli/src/commands/stake/claim.py | 113 +++------------- bittensor_cli/src/commands/stake/list.py | 4 +- bittensor_cli/src/commands/stake/remove.py | 2 - .../src/commands/subnets/mechanisms.py | 63 ++++----- bittensor_cli/src/commands/subnets/price.py | 7 +- bittensor_cli/src/commands/subnets/subnets.py | 34 ++--- bittensor_cli/src/commands/sudo.py | 7 +- bittensor_cli/src/commands/wallets.py | 33 +++-- bittensor_cli/src/commands/weights.py | 14 +- 22 files changed, 244 insertions(+), 404 deletions(-) diff --git a/bittensor_cli/src/bittensor/json_utils.py b/bittensor_cli/src/bittensor/json_utils.py index e094fa88b..f789e33df 100644 --- a/bittensor_cli/src/bittensor/json_utils.py +++ b/bittensor_cli/src/bittensor/json_utils.py @@ -231,6 +231,30 @@ def print_json_data(data: Any) -> None: json_console.print_json(data=data) +def print_transaction_with_data( + success: bool, + message: Optional[str] = None, + extrinsic_identifier: Optional[str] = None, + **extra_data: Any, +) -> None: + """ + Print a transaction response with additional data fields. + + Args: + success: Whether the transaction succeeded + message: Human-readable status message + extrinsic_identifier: The extrinsic ID (e.g., "12345678-2") + **extra_data: Additional fields to include in the response + """ + response = { + "success": success, + "message": message, + "extrinsic_identifier": extrinsic_identifier, + **extra_data, + } + json_console.print_json(data=response) + + def serialize_balance(balance: Any) -> dict[str, Union[int, float]]: """ Serialize a Balance object to a consistent dictionary format. diff --git a/bittensor_cli/src/commands/axon/axon.py b/bittensor_cli/src/commands/axon/axon.py index ebd7404cd..a904ff096 100644 --- a/bittensor_cli/src/commands/axon/axon.py +++ b/bittensor_cli/src/commands/axon/axon.py @@ -2,15 +2,12 @@ Axon commands for managing neuron serving endpoints. """ -import json from typing import TYPE_CHECKING from bittensor_wallet import Wallet -from bittensor_cli.src.bittensor.utils import ( - print_error, - json_console, -) +from bittensor_cli.src.bittensor.utils import print_error +from bittensor_cli.src.bittensor.json_utils import print_transaction_with_data from bittensor_cli.src.bittensor.extrinsics.serving import ( reset_axon_extrinsic, set_axon_extrinsic, @@ -54,16 +51,12 @@ async def reset( ) if json_output: - json_console.print( - json.dumps( - { - "success": success, - "message": message, - "extrinsic_identifier": ext_id, - "netuid": netuid, - "hotkey": wallet.hotkey.ss58_address, - } - ) + print_transaction_with_data( + success=success, + message=message, + extrinsic_identifier=ext_id, + netuid=netuid, + hotkey=wallet.hotkey.ss58_address, ) elif not success: print_error(f"Failed to reset axon: {message}") @@ -115,18 +108,14 @@ async def set_axon( ) if json_output: - json_console.print( - json.dumps( - { - "success": success, - "message": message, - "extrinsic_identifier": ext_id, - "netuid": netuid, - "hotkey": wallet.hotkey.ss58_address, - "ip": ip, - "port": port, - } - ) + print_transaction_with_data( + success=success, + message=message, + extrinsic_identifier=ext_id, + netuid=netuid, + hotkey=wallet.hotkey.ss58_address, + ip=ip, + port=port, ) elif not success: print_error(f"Failed to set axon: {message}") diff --git a/bittensor_cli/src/commands/crowd/contribute.py b/bittensor_cli/src/commands/crowd/contribute.py index 933e794a6..4bd979160 100644 --- a/bittensor_cli/src/commands/crowd/contribute.py +++ b/bittensor_cli/src/commands/crowd/contribute.py @@ -1,4 +1,3 @@ -import json from typing import Optional from async_substrate_interface.utils.cache import asyncio @@ -13,11 +12,11 @@ from bittensor_cli.src.bittensor.utils import ( confirm_action, console, - json_console, print_error, print_extrinsic_id, unlock_key, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data from bittensor_cli.src.commands.crowd.view import show_crowdloan_details from bittensor_cli.src.bittensor.chain_data import CrowdloanData @@ -87,7 +86,7 @@ async def contribute_to_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} not found." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -97,7 +96,7 @@ async def contribute_to_crowdloan( ) if not is_valid: if json_output: - json_console.print(json.dumps({"success": False, "error": error_message})) + print_json_data({"success": False, "error": error_message}) else: print_error(f"[red]{error_message}[/red]") return False, error_message @@ -134,7 +133,7 @@ async def contribute_to_crowdloan( if contribution_amount < crowdloan.min_contribution: error_msg = f"Contribution amount ({contribution_amount}) is below minimum ({crowdloan.min_contribution})." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Contribution below minimum requirement." @@ -142,7 +141,7 @@ async def contribute_to_crowdloan( if contribution_amount > user_balance: error_msg = f"Insufficient balance. You have {user_balance} but trying to contribute {contribution_amount}." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Insufficient balance." @@ -229,7 +228,7 @@ async def contribute_to_crowdloan( "\nProceed with contribution?", decline=decline, quiet=quiet ): if json_output: - json_console.print( + print_json_data( json.dumps( {"success": False, "error": "Contribution cancelled by user."} ) @@ -241,7 +240,7 @@ async def contribute_to_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -263,7 +262,7 @@ async def contribute_to_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -318,7 +317,7 @@ async def contribute_to_crowdloan( else False, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: console.print( f"\n[dark_sea_green3]Successfully contributed to crowdloan #{crowdloan_id}![/dark_sea_green3]" @@ -396,7 +395,7 @@ async def withdraw_from_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} does not exist." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -404,7 +403,7 @@ async def withdraw_from_crowdloan( if crowdloan.finalized: error_msg = f"Crowdloan #{crowdloan_id} is already finalized. Withdrawals are not allowed." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Cannot withdraw from finalized crowdloan." @@ -422,7 +421,7 @@ async def withdraw_from_crowdloan( f"You have no contribution to withdraw from crowdloan #{crowdloan_id}." ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "No contribution to withdraw." @@ -433,7 +432,7 @@ async def withdraw_from_crowdloan( if withdrawable <= 0: error_msg = f"As the creator, you cannot withdraw your deposit of {crowdloan.deposit}. Only contributions above the deposit can be withdrawn." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Creator cannot withdraw deposit amount." @@ -518,7 +517,7 @@ async def withdraw_from_crowdloan( "\nProceed with withdrawal?", decline=decline, quiet=quiet ): if json_output: - json_console.print( + print_json_data( json.dumps( {"success": False, "error": "Withdrawal cancelled by user."} ) @@ -530,7 +529,7 @@ async def withdraw_from_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -552,7 +551,7 @@ async def withdraw_from_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -600,7 +599,7 @@ async def withdraw_from_crowdloan( }, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: console.print( f"\n✅ [green]Successfully withdrew from crowdloan #{crowdloan_id}![/green]\n" diff --git a/bittensor_cli/src/commands/crowd/create.py b/bittensor_cli/src/commands/crowd/create.py index ff64e41a0..a75a5e77e 100644 --- a/bittensor_cli/src/commands/crowd/create.py +++ b/bittensor_cli/src/commands/crowd/create.py @@ -1,5 +1,4 @@ import asyncio -import json from typing import Optional from bittensor_wallet import Wallet @@ -16,12 +15,12 @@ blocks_to_duration, confirm_action, console, - json_console, print_error, is_valid_ss58_address, unlock_key, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data async def create_crowdloan( @@ -51,9 +50,7 @@ async def create_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( - json.dumps({"success": False, "error": unlock_status.message}) - ) + print_json_data({"success": False, "error": unlock_status.message}) else: print_error(f"[red]{unlock_status.message}[/red]") return False, unlock_status.message @@ -88,7 +85,7 @@ async def create_crowdloan( else: error_msg = "Crowdloan type not specified and no prompt provided." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(error_msg) return False, error_msg @@ -126,7 +123,7 @@ async def create_crowdloan( + ", ".join(missing_fields) ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Missing required options when prompts are disabled." @@ -148,7 +145,7 @@ async def create_crowdloan( continue error_msg = f"Deposit is below the minimum required deposit ({minimum_deposit.tao} TAO)." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Deposit is below the minimum required deposit." @@ -355,7 +352,7 @@ async def create_crowdloan( "Proceed with creating the crowdloan?", decline=decline, quiet=quiet ): if json_output: - json_console.print( + print_json_data( json.dumps( {"success": False, "error": "Cancelled crowdloan creation."} ) @@ -374,7 +371,7 @@ async def create_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -409,7 +406,7 @@ async def create_crowdloan( else: output_dict["data"]["target_address"] = target_address - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) message = f"{crowdloan_type.capitalize()} crowdloan created successfully." else: if crowdloan_type == "subnet": @@ -487,7 +484,7 @@ async def finalize_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} does not exist." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -497,7 +494,7 @@ async def finalize_crowdloan( f"Only the creator can finalize a crowdloan. Creator: {crowdloan.creator}" ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Only the creator can finalize a crowdloan." @@ -505,7 +502,7 @@ async def finalize_crowdloan( if crowdloan.finalized: error_msg = f"Crowdloan #{crowdloan_id} is already finalized." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, "Crowdloan is already finalized." @@ -517,7 +514,7 @@ async def finalize_crowdloan( f"Cap: {crowdloan.cap.tao}, Still needed: {still_needed.tao}" ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Crowdloan #{crowdloan_id} has not reached its cap.\n" @@ -606,7 +603,7 @@ async def finalize_crowdloan( "\nProceed with finalization?", decline=decline, quiet=quiet ): if json_output: - json_console.print( + print_json_data( json.dumps( {"success": False, "error": "Finalization cancelled by user."} ) @@ -618,7 +615,7 @@ async def finalize_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -635,7 +632,7 @@ async def finalize_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -664,7 +661,7 @@ async def finalize_crowdloan( "call_executed": crowdloan.has_call, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: console.print( f"\n[dark_sea_green3]Successfully finalized crowdloan #{crowdloan_id}![/dark_sea_green3]\n" diff --git a/bittensor_cli/src/commands/crowd/dissolve.py b/bittensor_cli/src/commands/crowd/dissolve.py index de134d964..ce13027cf 100644 --- a/bittensor_cli/src/commands/crowd/dissolve.py +++ b/bittensor_cli/src/commands/crowd/dissolve.py @@ -1,5 +1,4 @@ import asyncio -import json from typing import Optional from bittensor_wallet import Wallet @@ -12,11 +11,11 @@ blocks_to_duration, confirm_action, console, - json_console, print_extrinsic_id, print_error, unlock_key, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data async def dissolve_crowdloan( @@ -60,7 +59,7 @@ async def dissolve_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} not found." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -70,7 +69,7 @@ async def dissolve_crowdloan( f"Crowdloan #{crowdloan_id} is already finalized and cannot be dissolved." ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, f"Crowdloan #{crowdloan_id} is finalized." @@ -78,7 +77,7 @@ async def dissolve_crowdloan( if creator_ss58 != crowdloan.creator: error_msg = f"Only the creator can dissolve this crowdloan. Creator: {crowdloan.creator}, Your address: {creator_ss58}" if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Only the creator can dissolve this crowdloan.[/red]\n" @@ -98,7 +97,7 @@ async def dissolve_crowdloan( "Run 'btcli crowd refund' until only the creator's funds remain." ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Crowdloan still holds funds from other contributors.[/red]\n" @@ -146,7 +145,7 @@ async def dissolve_crowdloan( quiet=quiet, ): if json_output: - json_console.print( + print_json_data( json.dumps( {"success": False, "error": "Dissolution cancelled by user."} ) @@ -158,7 +157,7 @@ async def dissolve_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -187,7 +186,7 @@ async def dissolve_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -211,7 +210,7 @@ async def dissolve_crowdloan( "total_dissolved": creator_contribution.tao, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: await print_extrinsic_id(extrinsic_receipt) console.print("[green]Crowdloan dissolved successfully![/green]") diff --git a/bittensor_cli/src/commands/crowd/refund.py b/bittensor_cli/src/commands/crowd/refund.py index 6fbff652c..de81d554c 100644 --- a/bittensor_cli/src/commands/crowd/refund.py +++ b/bittensor_cli/src/commands/crowd/refund.py @@ -1,5 +1,4 @@ import asyncio -import json from typing import Optional from bittensor_wallet import Wallet @@ -10,11 +9,11 @@ from bittensor_cli.src.bittensor.utils import ( confirm_action, console, - json_console, print_extrinsic_id, print_error, unlock_key, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data from bittensor_cli.src.commands.crowd.view import show_crowdloan_details from bittensor_cli.src.commands.crowd.utils import get_constant @@ -62,7 +61,7 @@ async def refund_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} not found." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -70,7 +69,7 @@ async def refund_crowdloan( if crowdloan.finalized: error_msg = f"Crowdloan #{crowdloan_id} is already finalized. Finalized crowdloans cannot be refunded." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, f"Crowdloan #{crowdloan_id} is already finalized." @@ -78,7 +77,7 @@ async def refund_crowdloan( if creator_ss58 != crowdloan.creator: error_msg = f"Only the creator can refund this crowdloan. Creator: {crowdloan.creator}, Your address: {creator_ss58}" if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Only the creator can refund this crowdloan.[/red]\n" @@ -156,7 +155,7 @@ async def refund_crowdloan( quiet=quiet, ): if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": "Refund cancelled by user."}) ) else: @@ -166,7 +165,7 @@ async def refund_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -197,7 +196,7 @@ async def refund_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -223,7 +222,7 @@ async def refund_crowdloan( "amount_refunded": (crowdloan.raised - crowdloan.deposit).tao, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: console.print( f"[green]Contributors have been refunded for Crowdloan #{crowdloan_id}.[/green]" diff --git a/bittensor_cli/src/commands/crowd/update.py b/bittensor_cli/src/commands/crowd/update.py index 6abefed55..47d642d15 100644 --- a/bittensor_cli/src/commands/crowd/update.py +++ b/bittensor_cli/src/commands/crowd/update.py @@ -1,5 +1,4 @@ import asyncio -import json from typing import Optional, Union from bittensor_wallet import Wallet @@ -13,11 +12,11 @@ blocks_to_duration, confirm_action, console, - json_console, print_error, unlock_key, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data from bittensor_cli.src.commands.crowd.view import show_crowdloan_details from bittensor_cli.src.commands.crowd.utils import get_constant @@ -73,7 +72,7 @@ async def update_crowdloan( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} not found." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -83,7 +82,7 @@ async def update_crowdloan( f"Crowdloan #{crowdloan_id} is already finalized and cannot be updated." ) if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, f"Crowdloan #{crowdloan_id} is already finalized." @@ -92,7 +91,7 @@ async def update_crowdloan( if creator_address != crowdloan.creator: error_msg = "Only the creator can update this crowdloan." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Only the creator can update this crowdloan.[/red]\n" @@ -127,7 +126,7 @@ async def update_crowdloan( if choice == 4: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": "Update cancelled by user."}) ) else: @@ -243,7 +242,7 @@ async def update_crowdloan( if call_function is None or value is None or param_name is None: error_msg = "No update parameter specified." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -253,7 +252,7 @@ async def update_crowdloan( if value.rao < absolute_min.rao: error_msg = f"Minimum contribution must be at least {absolute_min}." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Minimum contribution ({value}) must be at least {absolute_min}.[/red]" @@ -264,7 +263,7 @@ async def update_crowdloan( if value <= current_block: error_msg = "End block must be in the future." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]End block ({value:,}) must be after current block ({current_block:,}).[/red]" @@ -275,7 +274,7 @@ async def update_crowdloan( if block_duration < min_duration: error_msg = "Block duration too short." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Duration ({blocks_to_duration(block_duration)}) is too short. " @@ -286,7 +285,7 @@ async def update_crowdloan( if block_duration > max_duration: error_msg = "Block duration too long." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]Duration ({blocks_to_duration(block_duration)}) is too long. " @@ -298,7 +297,7 @@ async def update_crowdloan( if value < crowdloan.raised: error_msg = "Cap must be >= raised amount." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error( f"[red]New cap ({value}) must be at least the amount already raised ({crowdloan.raised}).[/red]" @@ -341,7 +340,7 @@ async def update_crowdloan( quiet=quiet, ): if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": "Update cancelled by user."}) ) else: @@ -351,7 +350,7 @@ async def update_crowdloan( unlock_status = unlock_key(wallet) if not unlock_status.success: if json_output: - json_console.print( + print_json_data( json.dumps({"success": False, "error": unlock_status.message}) ) else: @@ -384,7 +383,7 @@ async def update_crowdloan( if not success: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -407,7 +406,7 @@ async def update_crowdloan( "update_type": update_type, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) else: console.print( f"[green]{update_type} updated successfully![/green]\n" diff --git a/bittensor_cli/src/commands/crowd/view.py b/bittensor_cli/src/commands/crowd/view.py index 9a248d18f..1262570e4 100644 --- a/bittensor_cli/src/commands/crowd/view.py +++ b/bittensor_cli/src/commands/crowd/view.py @@ -1,7 +1,6 @@ from typing import Optional import asyncio -import json from bittensor_wallet import Wallet from rich import box from rich.table import Column, Table @@ -13,10 +12,10 @@ from bittensor_cli.src.bittensor.utils import ( blocks_to_duration, console, - json_console, print_error, millify_tao, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data def _shorten(account: Optional[str]) -> str: @@ -57,7 +56,7 @@ async def list_crowdloans( ) if not loans: if json_output: - json_console.print( + print_json_data( json.dumps( { "success": True, @@ -147,7 +146,7 @@ async def list_crowdloans( "network": subtensor.network, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) return True if not verbose: @@ -338,7 +337,7 @@ async def show_crowdloan_details( if not crowdloan: error_msg = f"Crowdloan #{crowdloan_id} not found." if json_output: - json_console.print(json.dumps({"success": False, "error": error_msg})) + print_json_data({"success": False, "error": error_msg}) else: print_error(f"[red]{error_msg}[/red]") return False, error_msg @@ -437,7 +436,7 @@ async def show_crowdloan_details( "network": subtensor.network, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) return True, f"Displayed info for crowdloan #{crowdloan_id}" table = Table( diff --git a/bittensor_cli/src/commands/liquidity/liquidity.py b/bittensor_cli/src/commands/liquidity/liquidity.py index 6ad87ef13..31eef42fd 100644 --- a/bittensor_cli/src/commands/liquidity/liquidity.py +++ b/bittensor_cli/src/commands/liquidity/liquidity.py @@ -1,5 +1,4 @@ import asyncio -import json from typing import TYPE_CHECKING, Optional from async_substrate_interface import AsyncExtrinsicReceipt @@ -11,7 +10,6 @@ unlock_key, console, print_error, - json_console, print_extrinsic_id, ) from bittensor_cli.src.bittensor.json_utils import ( @@ -501,9 +499,7 @@ async def show_liquidity_list( (success, err_msg, positions) = liquidity_list_ if not success: if json_output: - json_console.print( - json.dumps({"success": success, "err_msg": err_msg, "positions": []}) - ) + print_json_data({"success": success, "err_msg": err_msg, "positions": []}) return else: print_error(f"Error: {err_msg}") diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index bbca6d0e0..28ca36f98 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -9,13 +9,16 @@ from bittensor_cli.src.bittensor.utils import ( confirm_action, print_extrinsic_id, - json_console, console, print_error, unlock_key, ProxyAddressBook, is_valid_ss58_address_prompt, ) +from bittensor_cli.src.bittensor.json_utils import ( + print_transaction_response, + print_transaction_with_data, +) if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -85,25 +88,13 @@ async def submit_proxy( ) if success: if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) + print_transaction_response(success, msg, await receipt.get_extrinsic_identifier()) else: await print_extrinsic_id(receipt) console.print(":white_check_mark:[green]Success![/green]") else: if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "extrinsic_identifier": None, - } - ) + print_transaction_response(success, msg) else: print_error(f"Failed: {msg}") @@ -144,13 +135,7 @@ async def create_proxy( if not json_output: print_error(ulw.message) else: - json_console.print_json( - data={ - "success": ulw.success, - "message": ulw.message, - "extrinsic_identifier": None, - } - ) + print_transaction_response(ulw.success, ulw.message) return None call = await subtensor.substrate.compose_call( call_module="Proxy", @@ -185,18 +170,16 @@ async def create_proxy( ) if json_output: - json_console.print_json( + print_transaction_with_data( + success, + msg, + await receipt.get_extrinsic_identifier(), data={ - "success": success, - "message": msg, - "data": { - "pure": created_pure, - "spawner": created_spawner, - "proxy_type": created_proxy_type.value, - "delay": delay, - }, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } + "pure": created_pure, + "spawner": created_spawner, + "proxy_type": created_proxy_type.value, + "delay": delay, + }, ) else: await print_extrinsic_id(receipt) @@ -237,14 +220,7 @@ async def create_proxy( return None else: if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": None, - } - ) + print_transaction_with_data(success, msg, None, data=None) else: print_error(f"Failed to create pure proxy: {msg}") return None @@ -279,13 +255,7 @@ async def remove_proxy( if not json_output: print_error(ulw.message) else: - json_console.print_json( - data={ - "success": ulw.success, - "message": ulw.message, - "extrinsic_identifier": None, - } - ) + print_transaction_response(ulw.success, ulw.message) return None call = await subtensor.substrate.compose_call( call_module="Proxy", @@ -344,13 +314,7 @@ async def add_proxy( if not json_output: print_error(ulw.message) else: - json_console.print_json( - data={ - "success": ulw.success, - "message": ulw.message, - "extrinsic_identifier": None, - } - ) + print_transaction_response(ulw.success, ulw.message) return None call = await subtensor.substrate.compose_call( call_module="Proxy", @@ -387,18 +351,16 @@ async def add_proxy( ) if json_output: - json_console.print_json( + print_transaction_with_data( + success, + msg, + await receipt.get_extrinsic_identifier(), data={ - "success": success, - "message": msg, - "data": { - "delegatee": delegatee, - "delegator": delegator, - "proxy_type": created_proxy_type.value, - "delay": delay, - }, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } + "delegatee": delegatee, + "delegator": delegator, + "proxy_type": created_proxy_type.value, + "delay": delay, + }, ) else: await print_extrinsic_id(receipt) @@ -438,14 +400,7 @@ async def add_proxy( ) else: if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": None, - } - ) + print_transaction_with_data(success, msg, None, data=None) else: print_error(f"Failed to add proxy: {msg}") return None @@ -483,13 +438,7 @@ async def kill_proxy( if not json_output: print_error(ulw.message) else: - json_console.print_json( - data={ - "success": ulw.success, - "message": ulw.message, - "extrinsic_identifier": None, - } - ) + print_transaction_response(ulw.success, ulw.message) return None spawner = spawner or wallet.coldkeypub.ss58_address call = await subtensor.substrate.compose_call( @@ -643,21 +592,13 @@ async def execute_announced( ) if success is True: if json_output: - json_console.print_json( - data={ - "success": True, - "message": msg, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) + print_transaction_response(True, msg, await receipt.get_extrinsic_identifier()) else: console.print(":white_check_mark:[green]Success![/green]") await print_extrinsic_id(receipt) else: if json_output: - json_console.print_json( - data={"success": False, "message": msg, "extrinsic_identifier": None} - ) + print_transaction_response(False, msg) else: print_error(f"Failed. {msg} ") return success diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index f967d08f9..3995b8586 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -22,7 +22,6 @@ print_error, print_verbose, unlock_key, - json_console, get_hotkey_pub_ss58, print_extrinsic_id, ) diff --git a/bittensor_cli/src/commands/stake/auto_staking.py b/bittensor_cli/src/commands/stake/auto_staking.py index 9fb10847a..1d99a6f2c 100644 --- a/bittensor_cli/src/commands/stake/auto_staking.py +++ b/bittensor_cli/src/commands/stake/auto_staking.py @@ -10,13 +10,13 @@ from bittensor_cli.src.bittensor.utils import ( confirm_action, console, - json_console, get_subnet_name, is_valid_ss58_address, print_error, unlock_key, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data, print_transaction_with_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -123,7 +123,7 @@ def resolve_identity(hotkey: str) -> Optional[str]: ) if json_output: - json_console.print(json.dumps(data_output)) + print_json_data(data_output) return data_output table = Table( @@ -282,16 +282,12 @@ async def set_auto_stake_destination( ext_id = await ext_receipt.get_extrinsic_identifier() if success else None if json_output: - json_console.print( - json.dumps( - { - "success": success, - "error": error_message, - "netuid": netuid, - "hotkey": hotkey_ss58, - "extrinsic_identifier": ext_id, - } - ) + print_transaction_with_data( + success=success, + message=error_message, + extrinsic_identifier=ext_id, + netuid=netuid, + hotkey=hotkey_ss58, ) if success: diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index d4fb17998..0c8c2b191 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -21,10 +21,10 @@ is_valid_ss58_address, format_error_message, unlock_key, - json_console, get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data async def get_childkey_completion_block( @@ -610,7 +610,7 @@ async def set_children( ":white_heavy_check_mark: [green]Sent set children request for all subnets.[/green]" ) if json_output: - json_console.print(json.dumps(successes)) + print_json_data(successes) async def revoke_children( @@ -701,7 +701,7 @@ async def revoke_children( f"Childkey revocation failed for netuid {netuid_}: {message}." ) if json_output: - json_console.print(json.dumps(dict_output)) + print_json_data(dict_output) async def childkey_take( diff --git a/bittensor_cli/src/commands/stake/claim.py b/bittensor_cli/src/commands/stake/claim.py index d07f4715b..9d797340f 100644 --- a/bittensor_cli/src/commands/stake/claim.py +++ b/bittensor_cli/src/commands/stake/claim.py @@ -1,5 +1,4 @@ import asyncio -import json from enum import Enum from typing import TYPE_CHECKING, Optional @@ -17,11 +16,15 @@ print_error, unlock_key, print_extrinsic_id, - json_console, millify_tao, group_subnets, parse_subnet_range, ) +from bittensor_cli.src.bittensor.json_utils import ( + print_json_data, + print_transaction_response, + print_transaction_with_data, +) if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -86,7 +89,7 @@ async def set_claim_type( msg = f"Invalid netuid format: {e}" print_error(msg) if json_output: - json_console.print(json.dumps({"success": False, "message": msg})) + print_transaction_response(False, msg) return False, msg, None claim_table = Table( @@ -114,15 +117,7 @@ async def set_claim_type( msg = "Operation cancelled." console.print(f"[yellow]{msg}[/yellow]") if json_output: - json_console.print( - json.dumps( - { - "success": False, - "message": msg, - "extrinsic_identifier": None, - } - ) - ) + print_transaction_response(False, msg, None) return False, msg, None # Keep netuids passed thru the cli and assume Keep type @@ -142,7 +137,7 @@ async def set_claim_type( msg = f"Invalid subnets (not available): {group_subnets(invalid)}" print_error(msg) if json_output: - json_console.print(json.dumps({"success": False, "message": msg})) + print_transaction_response(False, msg) return False, msg, None if not keep_subnets: @@ -159,15 +154,7 @@ async def set_claim_type( msg = f"Claim type already set to {_format_claim_type_display(new_claim_info)}. \nNo change needed." console.print(msg) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "message": msg, - "extrinsic_identifier": None, - } - ) - ) + print_transaction_response(True, msg, None) return True, msg, None if prompt: @@ -185,14 +172,14 @@ async def set_claim_type( msg = "Operation cancelled." console.print(f"[yellow]{msg}[/yellow]") if json_output: - json_console.print(json.dumps({"success": False, "message": msg})) + print_transaction_response(False, msg) return False, msg, None if not (unlock := unlock_key(wallet)).success: msg = f"Failed to unlock wallet: {unlock.message}" print_error(msg) if json_output: - json_console.print(json.dumps({"success": False, "message": msg})) + print_transaction_response(False, msg) return False, msg, None with console.status(":satellite: Setting root claim type...", spinner="earth"): @@ -212,21 +199,13 @@ async def set_claim_type( console.print(f":white_heavy_check_mark: [green]{msg}[/green]") await print_extrinsic_id(ext_receipt) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "message": msg, - "extrinsic_identifier": ext_id, - } - ) - ) + print_transaction_response(True, msg, ext_id) return True, msg, ext_id else: msg = f"Failed to set claim type: {err_msg}" print_error(msg) if json_output: - json_console.print(json.dumps({"success": False, "message": msg})) + print_transaction_response(False, msg) return False, msg, None @@ -255,16 +234,7 @@ async def process_pending_claims( msg = "No stakes found for this coldkey" console.print(f"[yellow]{msg}[/yellow]") if json_output: - json_console.print( - json.dumps( - { - "success": True, - "message": msg, - "extrinsic_identifier": None, - "netuids": [], - } - ) - ) + print_transaction_with_data(True, msg, None, netuids=[]) return True, msg, None current_stakes = { @@ -316,16 +286,7 @@ async def process_pending_claims( msg = "No claimable emissions found" console.print(f"[yellow]{msg}[/yellow]") if json_output: - json_console.print( - json.dumps( - { - "success": True, - "message": msg, - "extrinsic_identifier": None, - "netuids": netuids, - } - ) - ) + print_transaction_with_data(True, msg, None, netuids=netuids) return True, msg, None _print_claimable_table(wallet, claimable_stake_info, verbose) @@ -351,32 +312,14 @@ async def process_pending_claims( msg = "Operation cancelled by user" console.print(f"[yellow]{msg}[/yellow]") if json_output: - json_console.print( - json.dumps( - { - "success": False, - "message": msg, - "extrinsic_identifier": None, - "netuids": selected_netuids, - } - ) - ) + print_transaction_with_data(False, msg, None, netuids=selected_netuids) return False, msg, None if not (unlock := unlock_key(wallet)).success: msg = f"Failed to unlock wallet: {unlock.message}" print_error(msg) if json_output: - json_console.print( - json.dumps( - { - "success": False, - "message": msg, - "extrinsic_identifier": None, - "netuids": selected_netuids, - } - ) - ) + print_transaction_with_data(False, msg, None, netuids=selected_netuids) return False, msg, None with console.status( @@ -392,31 +335,13 @@ async def process_pending_claims( console.print(f"[dark_sea_green3]{msg}[/dark_sea_green3]") await print_extrinsic_id(ext_receipt) if json_output: - json_console.print( - json.dumps( - { - "success": True, - "message": msg, - "extrinsic_identifier": ext_id, - "netuids": selected_netuids, - } - ) - ) + print_transaction_with_data(True, msg, ext_id, netuids=selected_netuids) return True, msg, ext_id else: msg = f"Failed to claim root emissions: {err_msg}" print_error(msg) if json_output: - json_console.print( - json.dumps( - { - "success": False, - "message": msg, - "extrinsic_identifier": None, - "netuids": selected_netuids, - } - ) - ) + print_transaction_with_data(False, msg, None, netuids=selected_netuids) return False, msg, None diff --git a/bittensor_cli/src/commands/stake/list.py b/bittensor_cli/src/commands/stake/list.py index d4a087970..6acb61e9f 100644 --- a/bittensor_cli/src/commands/stake/list.py +++ b/bittensor_cli/src/commands/stake/list.py @@ -19,8 +19,8 @@ print_error, millify_tao, get_subnet_name, - json_console, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -651,7 +651,7 @@ def format_cell( dict_output["total_tao_value"] = all_hks_tao_value.tao + balance.tao dict_output["total_swapped_tao_value"] = all_hks_swapped_tao_value.tao if json_output: - json_console.print(json.dumps(dict_output)) + print_json_data(dict_output) if not sub_stakes: console.print( f"\n[blue]No stakes found for coldkey ss58: ({coldkey_address})" diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index 90c8de22f..a0657734d 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -1,5 +1,4 @@ import asyncio -import json from functools import partial from typing import TYPE_CHECKING, Optional @@ -25,7 +24,6 @@ format_error_message, group_subnets, unlock_key, - json_console, get_hotkey_pub_ss58, print_extrinsic_id, ) diff --git a/bittensor_cli/src/commands/subnets/mechanisms.py b/bittensor_cli/src/commands/subnets/mechanisms.py index 2400be707..88057f7e1 100644 --- a/bittensor_cli/src/commands/subnets/mechanisms.py +++ b/bittensor_cli/src/commands/subnets/mechanisms.py @@ -13,10 +13,10 @@ confirm_action, console, print_error, - json_console, U16_MAX, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -33,8 +33,7 @@ async def count( if not await subtensor.subnet_exists(netuid=netuid, block_hash=block_hash): print_error(f"Subnet {netuid} does not exist") if json_output: - json_console.print_json( - data={"success": False, "error": f"Subnet {netuid} does not exist"} + print_json_data({"success": False, "error": f"Subnet {netuid} does not exist"} ) return None @@ -47,13 +46,11 @@ async def count( ) if not mechanism_count: if json_output: - json_console.print_json( - data={ - "netuid": netuid, - "count": None, - "error": "Failed to get mechanism count", - } - ) + print_json_data({ + "netuid": netuid, + "count": None, + "error": "Failed to get mechanism count", + }) else: print_error( "Subnet mechanism count: [red]Failed to get mechanism count[/red]" @@ -61,13 +58,11 @@ async def count( return None if json_output: - json_console.print_json( - data={ - "netuid": netuid, - "count": mechanism_count, - "error": "", - } - ) + print_json_data({ + "netuid": netuid, + "count": mechanism_count, + "error": "", + }) else: console.print( f"[blue]Subnet {netuid}[/blue] currently has [blue]{mechanism_count}[/blue] mechanism" @@ -91,8 +86,7 @@ async def get_emission_split( f"Subnet {netuid} only has the primary mechanism (mechanism 0). No emission split to display." ) if json_output: - json_console.print_json( - data={ + print_json_data({ "success": False, "error": "Subnet only has the primary mechanism (mechanism 0). No emission split to display.", } @@ -127,7 +121,7 @@ async def get_emission_split( } if json_output: - json_console.print_json(data=data) + print_json_data(data) else: table = Table( Column( @@ -203,7 +197,7 @@ async def set_emission_split( f"Subnet {netuid} does not currently contain any mechanisms to configure." ) if json_output: - json_console.print_json(data={"success": False, "error": message}) + print_json_data({"success": False, "error": message}) else: print_error(message) return False @@ -231,7 +225,7 @@ async def set_emission_split( "Invalid `--split` values. Provide a comma-separated list of numbers." ) if json_output: - json_console.print_json(data={"success": False, "error": message}) + print_json_data({"success": False, "error": message}) else: print_error(message) return False @@ -270,7 +264,7 @@ async def set_emission_split( if len(weights) != mech_count: message = f"Expected {mech_count} weight values, received {len(weights)}." if json_output: - json_console.print_json(data={"success": False, "error": message}) + print_json_data({"success": False, "error": message}) else: print_error(message) return False @@ -278,7 +272,7 @@ async def set_emission_split( if any(value < 0 for value in weights): message = "Weights must be non-negative." if json_output: - json_console.print_json(data={"success": False, "error": message}) + print_json_data({"success": False, "error": message}) else: print_error(message) return False @@ -288,7 +282,7 @@ async def set_emission_split( except ValueError as exc: message = str(exc) if json_output: - json_console.print_json(data={"success": False, "error": message}) + print_json_data({"success": False, "error": message}) else: print_error(message) return False @@ -296,8 +290,7 @@ async def set_emission_split( if normalized_weights == existing_split: message = ":white_heavy_check_mark: [dark_sea_green3]Emission split unchanged.[/dark_sea_green3]" if json_output: - json_console.print_json( - data={ + print_json_data({ "success": True, "message": "Emission split unchanged.", "split": normalized_weights, @@ -372,15 +365,13 @@ async def set_emission_split( ) if json_output: - json_console.print_json( - data={ - "success": success, - "err_msg": err_msg, - "split": normalized_weights, - "percentages": [round(value * 100, 6) for value in fractions], - "extrinsic_identifier": ext_id, - } - ) + print_json_data({ + "success": success, + "err_msg": err_msg, + "split": normalized_weights, + "percentages": [round(value * 100, 6) for value in fractions], + "extrinsic_identifier": ext_id, + }) return success diff --git a/bittensor_cli/src/commands/subnets/price.py b/bittensor_cli/src/commands/subnets/price.py index 7f886f2be..c348e292c 100644 --- a/bittensor_cli/src/commands/subnets/price.py +++ b/bittensor_cli/src/commands/subnets/price.py @@ -1,5 +1,4 @@ import asyncio -import json import math import tempfile import webbrowser @@ -15,9 +14,9 @@ console, get_subnet_name, print_error, - json_console, jinja_env, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -90,7 +89,7 @@ async def price( subnet_data, block_numbers, interval_hours, log_scale ) elif json_output: - json_console.print(json.dumps(_generate_json_output(subnet_data))) + print_json_data(_generate_json_output(subnet_data)) else: _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale) else: @@ -103,7 +102,7 @@ async def price( all_subnet_info, netuids, all_netuids ) if json_output: - json_console.print(json.dumps(_generate_json_output(subnet_data))) + print_json_data(_generate_json_output(subnet_data)) else: _generate_cli_output_current(subnet_data) diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py index ad0404b4f..760ca8022 100644 --- a/bittensor_cli/src/commands/subnets/subnets.py +++ b/bittensor_cli/src/commands/subnets/subnets.py @@ -40,11 +40,11 @@ get_subnet_name, unlock_key, blocks_to_duration, - json_console, get_hotkey_pub_ss58, print_extrinsic_id, check_img_mimetype, ) +from bittensor_cli.src.bittensor.json_utils import print_json_data if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -981,7 +981,7 @@ def format_liquidity_cell( # Non-live mode subnets, block_number, mechanisms, ema_tao_inflow = await fetch_subnet_data() if json_output: - json_console.print( + print_json_data( json.dumps( dict_table(subnets, block_number, mechanisms, ema_tao_inflow) ) @@ -1600,7 +1600,7 @@ async def show_subnet( "uids": json_out_rows, } if json_output: - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) mech_line = ( f"\n Mechanism ID: [{COLOR_PALETTE['GENERAL']['SUBHEADING_EXTRA_1']}]#{selected_mechanism_id}" @@ -1698,7 +1698,7 @@ async def burn_cost( current_burn_cost = await subtensor.burn_cost() if current_burn_cost: if json_output: - json_console.print( + print_json_data( json.dumps({"burn_cost": current_burn_cost.to_dict(), "error": ""}) ) else: @@ -1708,7 +1708,7 @@ async def burn_cost( return current_burn_cost else: if json_output: - json_console.print( + print_json_data( json.dumps( {"burn_cost": None, "error": "Failed to get subnet burn cost"} ) @@ -1745,7 +1745,7 @@ async def create( 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( + print_json_data( json.dumps( {"success": success, "netuid": netuid, "extrinsic_identifier": ext_id} ) @@ -1859,7 +1859,7 @@ async def _storage_key(storage_fn: str) -> StorageKey: if not await subtensor.subnet_exists(netuid=netuid, block_hash=block_hash): print_error(f"Subnet {netuid} does not exist") if json_output: - json_console.print_json( + print_json_data( data={ "success": False, "msg": f"Subnet {netuid} does not exist", @@ -1885,7 +1885,7 @@ async def _storage_key(storage_fn: str) -> StorageKey: err_msg = f"Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO" print_error(err_msg) if json_output: - json_console.print_json( + print_json_data( data={"success": False, "msg": err_msg, "extrinsic_identifier": None} ) return @@ -1995,7 +1995,7 @@ async def _storage_key(storage_fn: str) -> StorageKey: if not registration_allowed: print_error(f"Registration to subnet {netuid} is not allowed") if json_output: - json_console.print_json( + print_json_data( data={ "success": False, "msg": f"Registration to subnet {netuid} is not allowed", @@ -2012,7 +2012,7 @@ async def _storage_key(storage_fn: str) -> StorageKey: f"Try again in {remaining_blocks} blocks." ) if json_output: - json_console.print_json( + print_json_data( data={ "success": False, "msg": f"Registration to subnet {netuid} is full for this interval. " @@ -2022,7 +2022,7 @@ async def _storage_key(storage_fn: str) -> StorageKey: ) return if json_output: - json_console.print( + print_json_data( json.dumps({"success": success, "msg": msg, "extrinsic_identifier": ext_id}) ) @@ -2680,7 +2680,7 @@ async def get_identity( f" on {subtensor}" ) if json_output: - json_console.print("{}") + print_json_data("{}") return {} else: table = create_identity_table(title=title) @@ -2700,7 +2700,7 @@ 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)) + print_json_data(dict_out) else: console.print(table) return identity @@ -2848,7 +2848,7 @@ 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( + print_json_data( data={"success": False, "message": err, "extrinsic_identifier": None} ) else: @@ -2867,7 +2867,7 @@ async def set_symbol( if not (unlock_status := unlock_key(wallet, print_out=False)).success: err = unlock_status.message if json_output: - json_console.print_json(data={"success": False, "message": err}) + print_json_data({"success": False, "message": err}) else: console.print(err) return False @@ -2887,7 +2887,7 @@ async def set_symbol( await print_extrinsic_id(response) message = f"Successfully updated SN{netuid}'s symbol to {symbol}." if json_output: - json_console.print_json( + print_json_data( data={ "success": True, "message": message, @@ -2899,7 +2899,7 @@ async def set_symbol( return True else: if json_output: - json_console.print_json( + print_json_data( data={ "success": False, "message": err_msg, diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 24240d8e1..e51d00416 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -27,13 +27,12 @@ normalize_hyperparameters, unlock_key, blocks_to_duration, - json_console, string_to_u16, string_to_u64, get_hotkey_pub_ss58, print_extrinsic_id, ) -from bittensor_cli.src.bittensor.json_utils import print_transaction_response +from bittensor_cli.src.bittensor.json_utils import print_json_data, print_transaction_response if TYPE_CHECKING: from bittensor_cli.src.bittensor.subtensor_interface import ( @@ -1012,7 +1011,7 @@ async def get_senate( ) dict_output.append({"name": member_name, "ss58_address": ss58_address}) if json_output: - json_console.print(json.dumps(dict_output, ensure_ascii=True)) + print_json_data(dict_output) return console.print(table) @@ -1105,7 +1104,7 @@ async def proposals( } ) if json_output: - json_console.print(json.dumps(dict_output, ensure_ascii=True)) + print_json_data(dict_output) console.print(table) console.print( "\n[dim]* Both Ayes and Nays percentages are calculated relative to the proposal's threshold.[/dim]" diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 596c0d927..72d07194f 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -38,7 +38,6 @@ console, convert_blocks_to_time, err_console, - json_console, print_error, print_verbose, get_all_wallets_for_path, @@ -314,7 +313,7 @@ async def regen_hotkey_pub( f"coldkey ss58: ({new_hotkeypub.coldkeypub.ss58_address})", ) if json_output: - json_console.print( + print_json_data( json.dumps( { "success": True, @@ -332,7 +331,7 @@ async def regen_hotkey_pub( except KeyFileError: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( + print_json_data( '{"success": false, "error": "Keyfile is not writable", "data": null}' ) @@ -365,7 +364,7 @@ async def new_hotkey( ) console.print("[dark_sea_green]Hotkey created[/dark_sea_green]") if json_output: - json_console.print( + print_json_data( json.dumps( { "success": True, @@ -383,7 +382,7 @@ async def new_hotkey( except KeyFileError: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( + print_json_data( '{"success": false, "error": "Keyfile is not writable", "data": null}' ) @@ -416,7 +415,7 @@ async def new_coldkey( ) console.print("[dark_sea_green]Coldkey created[/dark_sea_green]") if json_output: - json_console.print( + print_json_data( json.dumps( { "success": True, @@ -432,7 +431,7 @@ async def new_coldkey( except KeyFileError as e: print_error("KeyFileError: File is not writable") if json_output: - json_console.print( + print_json_data( json.dumps( { "success": False, @@ -519,7 +518,7 @@ async def wallet_create( print_error(err) output_dict["error"] = err if json_output: - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) def get_coldkey_wallets_for_path(path: str) -> list[Wallet]: @@ -697,7 +696,7 @@ async def wallet_balance( "total": (total_free_balance + total_staked_balance).tao, }, } - json_console.print(json.dumps(output_dict)) + print_json_data(output_dict) return total_free_balance @@ -906,7 +905,7 @@ async def wallet_list( ) root.add(message) if json_output: - json_console.print(json.dumps(main_data_dict)) + print_json_data(main_data_dict) else: console.print(root) @@ -1339,7 +1338,7 @@ def overview_sort_function(row_): if not json_output: console.print(grid, width=None) else: - json_console.print(json.dumps(data_dict)) + print_json_data(data_dict) def _get_hotkeys( @@ -1848,7 +1847,7 @@ async def get_id( f" on {subtensor}" ) if json_output: - json_console.print("{}") + print_json_data("{}") return {} table = create_identity_table(title) @@ -1858,7 +1857,7 @@ async def get_id( console.print(table) if json_output: - json_console.print(json.dumps(identity)) + print_json_data(identity) return identity @@ -1924,7 +1923,7 @@ async def sign( console.print("[dark_sea_green3]Message signed successfully!\n") if json_output: - json_console.print( + print_json_data( json.dumps( {"signed_message": signed_message, "signer_address": signer_address} ) @@ -1964,7 +1963,7 @@ async def verify( except (ValueError, TypeError) as e: if json_output: - json_console.print( + print_json_data( json.dumps( { "verified": False, @@ -1982,7 +1981,7 @@ async def verify( signature_bytes = bytes.fromhex(signature.strip().lower().replace("0x", "")) except ValueError as e: if json_output: - json_console.print( + print_json_data( json.dumps( { "verified": False, @@ -1997,7 +1996,7 @@ async def verify( is_valid = keypair.verify(message.encode("utf-8"), signature_bytes) if json_output: - json_console.print( + print_json_data( json.dumps( {"verified": is_valid, "signer": signer_address, "message": message} ) diff --git a/bittensor_cli/src/commands/weights.py b/bittensor_cli/src/commands/weights.py index a12f5b659..56489efe7 100644 --- a/bittensor_cli/src/commands/weights.py +++ b/bittensor_cli/src/commands/weights.py @@ -14,10 +14,10 @@ print_error, console, format_error_message, - json_console, get_hotkey_pub_ss58, print_extrinsic_id, ) +from bittensor_cli.src.bittensor.json_utils import print_transaction_response from bittensor_cli.src.bittensor.extrinsics.root import ( convert_weights_and_uids_for_emit, generate_weight_hash, @@ -408,11 +408,7 @@ async def reveal_weights( ) success, message, ext_id = await extrinsic.reveal(weight_uids, weight_vals) if json_output: - json_console.print( - json.dumps( - {"success": success, "message": message, "extrinsic_identifier": ext_id} - ) - ) + print_transaction_response(success, message, ext_id) else: if success: console.print("Weights revealed successfully") @@ -458,11 +454,7 @@ async def commit_weights( ) success, message, ext_id = await extrinsic.set_weights_extrinsic() if json_output: - json_console.print( - json.dumps( - {"success": success, "message": message, "extrinsic_identifier": ext_id} - ) - ) + print_transaction_response(success, message, ext_id) else: if success: console.print("Weights set successfully") From fc51c580cd9e0423a62c50a3ad4378e43370802f Mon Sep 17 00:00:00 2001 From: DanielDerefaka Date: Tue, 30 Dec 2025 00:05:10 +0100 Subject: [PATCH 4/4] Fix remaining json_console usage from upstream merge Migrated one more json_console.print_json call in proxy.py that came from upstream merge to use print_transaction_response instead. --- bittensor_cli/src/commands/proxy.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index c1639304b..b9c560607 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -600,13 +600,10 @@ async def execute_announced( "You should rerun this command on an archive node endpoint." ) if json_output: - json_console.print_json( - data={ - "success": False, - "message": f"Unable to regenerate the call data using the latest runtime: {e}. " - "You should rerun this command on an archive node endpoint.", - "extrinsic_identifier": None, - } + print_transaction_response( + False, + f"Unable to regenerate the call data using the latest runtime: {e}. " + "You should rerun this command on an archive node endpoint.", ) return False