From 0d673ba32273dfb65e66803893ae3319a7fba203 Mon Sep 17 00:00:00 2001 From: bitloi Date: Thu, 9 Apr 2026 00:00:54 +0200 Subject: [PATCH 1/4] Fix proxy receipt parsing and address-book mapping --- bittensor_cli/cli.py | 12 +- bittensor_cli/src/commands/proxy.py | 232 ++++++++++++++++++++++++---- 2 files changed, 209 insertions(+), 35 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index def64c80d..510a72a82 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -2088,8 +2088,8 @@ def config_get_proxies(self) -> None: """ table = Table( Column("[bold white]Name", style=f"{COLORS.G.ARG}"), - Column("Address/Delegator", style="gold1"), - Column("Spawner/Delegatee", style="medium_purple"), + Column("Address/Delegatee", style="gold1"), + Column("Spawner/Delegator", style="medium_purple"), Column("Proxy Type", style="medium_purple"), Column("Delay", style="dim"), Column("Note", style="dim"), @@ -2174,8 +2174,8 @@ def config_update_proxy( ) table = Table( Column("[bold white]Name", style=f"{COLORS.G.ARG}"), - Column("Address/Delegator", style="gold1"), - Column("Spawner/Delegatee", style="medium_purple"), + Column("Address/Delegatee", style="gold1"), + Column("Spawner/Delegator", style="medium_purple"), Column("Proxy Type", style="medium_purple"), Column("Delay", style="dim"), Column("Note", style="dim"), @@ -2202,7 +2202,7 @@ def config_update_proxy( break console.print(table) console.print( - "\n\n[1] Address/Delegator" + "\n\n[1] Address/Delegatee" "\n[2] Spawner/Delegator" "\n[3] Proxy Type" "\n[4] Delay" @@ -10127,7 +10127,7 @@ def proxy_execute_announced( console.print( f"Name: {p_name}\n" f"Delay: {delay_}\n" - f"Spawner/Delegatee: {spawner}\n" + f"Spawner/Delegator: {spawner}\n" f"Proxy Type: {proxy_type}\n" f"Note: {note}\n" ) diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index 67c00bd0c..d63d8af20 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Any, Optional import sys from async_substrate_interface.errors import StateDiscardedError @@ -55,6 +55,71 @@ class ProxyType(StrEnum): RootClaim = "RootClaim" +async def extract_event_attributes_from_receipt( + receipt: Any, event_name: str +) -> Optional[dict[str, Any]]: + """Extracts event attributes from a receipt, supporting nested and top-level event shapes.""" + + for event in await receipt.triggered_events: + if not isinstance(event, dict): + continue + event_data = event.get("event", event) + if not isinstance(event_data, dict): + continue + if event_data.get("event_id") == event_name: + attributes = event_data.get("attributes") + if isinstance(attributes, dict): + return attributes + return None + return None + + +def write_proxy_address_book_entry( + conn: Any, + cursor: Any, + *, + name: str, + delay: int, + proxy_type: str, + note: str, + pure: Optional[str] = None, + spawner: Optional[str] = None, + delegatee: Optional[str] = None, + delegator: Optional[str] = None, +) -> None: + """Writes a normalized proxy address-book entry for pure and non-pure proxies.""" + + if pure is not None or spawner is not None: + if pure is None or spawner is None: + raise ValueError( + "Both `pure` and `spawner` must be supplied for a pure proxy." + ) + ss58_address = pure + spawner_address = spawner + elif delegatee is not None or delegator is not None: + if delegatee is None or delegator is None: + raise ValueError( + "Both `delegatee` and `delegator` must be supplied for a regular proxy." + ) + ss58_address = delegatee + spawner_address = delegator + else: + raise ValueError( + "Supply either (`pure`, `spawner`) or (`delegatee`, `delegator`)." + ) + + ProxyAddressBook.add_entry( + conn, + cursor, + name=name, + ss58_address=ss58_address, + delay=delay, + proxy_type=proxy_type, + note=note, + spawner=spawner_address, + ) + + # TODO add announce with also --reject and --remove @@ -170,15 +235,70 @@ async def create_proxy( ), ) if success: - created_pure = None - created_spawner = None - created_proxy_type = None - for event in await receipt.triggered_events: - if event["event_id"] == "PureCreated": - attrs = event["attributes"] - created_pure = attrs["pure"] - created_spawner = attrs["who"] - created_proxy_type = getattr(ProxyType, attrs["proxy_type"]) + attrs = await extract_event_attributes_from_receipt(receipt, "PureCreated") + if attrs is None: + msg = "Created pure proxy, but could not parse the `PureCreated` event from the receipt." + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + + created_pure = attrs.get("pure") + created_spawner = attrs.get("who") + created_proxy_type_name = attrs.get("proxy_type") + if not ( + isinstance(created_pure, str) + and isinstance(created_spawner, str) + and isinstance(created_proxy_type_name, str) + ): + msg = ( + "Created pure proxy, but received malformed `PureCreated` event attributes " + "from the receipt." + ) + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + + try: + created_proxy_type = ProxyType(created_proxy_type_name) + except ValueError: + msg = ( + "Created pure proxy, but received an unknown proxy type in the receipt: " + f"{created_proxy_type_name}." + ) + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + msg = ( f"Created pure '{created_pure}' " f"from spawner '{created_spawner}' " @@ -222,14 +342,14 @@ async def create_proxy( "[Optional] Add a note for this proxy", default="" ) with ProxyAddressBook.get_db() as (conn, cursor): - ProxyAddressBook.add_entry( - conn, - cursor, + write_proxy_address_book_entry( + conn=conn, + cursor=cursor, name=proxy_name, - ss58_address=created_pure, delay=delay, proxy_type=created_proxy_type.value, note=note, + pure=created_pure, spawner=created_spawner, ) console.print( @@ -398,16 +518,70 @@ async def add_proxy( era={"period": period}, ) if success: - delegatee = None - delegator = None - created_proxy_type = None - for event in await receipt.triggered_events: - if event["event_id"] == "ProxyAdded": - attrs = event["attributes"] - delegatee = attrs["delegatee"] - delegator = attrs["delegator"] - created_proxy_type = getattr(ProxyType, attrs["proxy_type"]) - break + attrs = await extract_event_attributes_from_receipt(receipt, "ProxyAdded") + if attrs is None: + msg = "Added proxy, but could not parse the `ProxyAdded` event from the receipt." + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + + delegatee = attrs.get("delegatee") + delegator = attrs.get("delegator") + created_proxy_type_name = attrs.get("proxy_type") + if not ( + isinstance(delegatee, str) + and isinstance(delegator, str) + and isinstance(created_proxy_type_name, str) + ): + msg = ( + "Added proxy, but received malformed `ProxyAdded` event attributes " + "from the receipt." + ) + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + + try: + created_proxy_type = ProxyType(created_proxy_type_name) + except ValueError: + msg = ( + "Added proxy, but received an unknown proxy type in the receipt: " + f"{created_proxy_type_name}." + ) + if json_output: + json_console.print_json( + data={ + "success": success, + "message": msg, + "data": None, + "extrinsic_identifier": await receipt.get_extrinsic_identifier(), + } + ) + else: + await print_extrinsic_id(receipt) + console.print(msg) + return None + msg = ( f"Added proxy delegatee '{delegatee}' " f"from delegator '{delegator}' " @@ -451,15 +625,15 @@ async def add_proxy( "[Optional] Add a note for this proxy", default="" ) with ProxyAddressBook.get_db() as (conn, cursor): - ProxyAddressBook.add_entry( - conn, - cursor, + write_proxy_address_book_entry( + conn=conn, + cursor=cursor, name=proxy_name, - ss58_address=delegator, delay=delay, proxy_type=created_proxy_type.value, note=note, - spawner=delegatee, + delegatee=delegatee, + delegator=delegator, ) console.print( f"Added to Proxy Address Book.\n" From cdf27fd865e833ae6bbf01b9e6493342fb9c5414 Mon Sep 17 00:00:00 2001 From: bitloi Date: Thu, 9 Apr 2026 16:21:47 +0200 Subject: [PATCH 2/4] Simplify proxy address-book write parameters --- bittensor_cli/src/commands/proxy.py | 35 ++++++----------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index d63d8af20..0d90bfdb6 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -79,34 +79,13 @@ def write_proxy_address_book_entry( cursor: Any, *, name: str, + ss58_address: str, delay: int, proxy_type: str, note: str, - pure: Optional[str] = None, - spawner: Optional[str] = None, - delegatee: Optional[str] = None, - delegator: Optional[str] = None, + spawner: str, ) -> None: - """Writes a normalized proxy address-book entry for pure and non-pure proxies.""" - - if pure is not None or spawner is not None: - if pure is None or spawner is None: - raise ValueError( - "Both `pure` and `spawner` must be supplied for a pure proxy." - ) - ss58_address = pure - spawner_address = spawner - elif delegatee is not None or delegator is not None: - if delegatee is None or delegator is None: - raise ValueError( - "Both `delegatee` and `delegator` must be supplied for a regular proxy." - ) - ss58_address = delegatee - spawner_address = delegator - else: - raise ValueError( - "Supply either (`pure`, `spawner`) or (`delegatee`, `delegator`)." - ) + """Writes a proxy address-book entry using the normalized storage schema.""" ProxyAddressBook.add_entry( conn, @@ -116,7 +95,7 @@ def write_proxy_address_book_entry( delay=delay, proxy_type=proxy_type, note=note, - spawner=spawner_address, + spawner=spawner, ) @@ -346,10 +325,10 @@ async def create_proxy( conn=conn, cursor=cursor, name=proxy_name, + ss58_address=created_pure, delay=delay, proxy_type=created_proxy_type.value, note=note, - pure=created_pure, spawner=created_spawner, ) console.print( @@ -629,11 +608,11 @@ async def add_proxy( conn=conn, cursor=cursor, name=proxy_name, + ss58_address=delegatee, delay=delay, proxy_type=created_proxy_type.value, note=note, - delegatee=delegatee, - delegator=delegator, + spawner=delegator, ) console.print( f"Added to Proxy Address Book.\n" From 48d2a56aa97c6c9f512fc6e39a844947a5599c1a Mon Sep 17 00:00:00 2001 From: bitloi Date: Thu, 9 Apr 2026 17:47:16 +0200 Subject: [PATCH 3/4] chore: rerun ci From c7f6756c1b3d27214b768207593e668ed43d9fb6 Mon Sep 17 00:00:00 2001 From: bitloi Date: Fri, 10 Apr 2026 10:08:16 +0200 Subject: [PATCH 4/4] Narrow proxy PR scope to mapping fix --- bittensor_cli/cli.py | 12 +- bittensor_cli/src/commands/proxy.py | 205 ++++------------------------ 2 files changed, 32 insertions(+), 185 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 510a72a82..def64c80d 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -2088,8 +2088,8 @@ def config_get_proxies(self) -> None: """ table = Table( Column("[bold white]Name", style=f"{COLORS.G.ARG}"), - Column("Address/Delegatee", style="gold1"), - Column("Spawner/Delegator", style="medium_purple"), + Column("Address/Delegator", style="gold1"), + Column("Spawner/Delegatee", style="medium_purple"), Column("Proxy Type", style="medium_purple"), Column("Delay", style="dim"), Column("Note", style="dim"), @@ -2174,8 +2174,8 @@ def config_update_proxy( ) table = Table( Column("[bold white]Name", style=f"{COLORS.G.ARG}"), - Column("Address/Delegatee", style="gold1"), - Column("Spawner/Delegator", style="medium_purple"), + Column("Address/Delegator", style="gold1"), + Column("Spawner/Delegatee", style="medium_purple"), Column("Proxy Type", style="medium_purple"), Column("Delay", style="dim"), Column("Note", style="dim"), @@ -2202,7 +2202,7 @@ def config_update_proxy( break console.print(table) console.print( - "\n\n[1] Address/Delegatee" + "\n\n[1] Address/Delegator" "\n[2] Spawner/Delegator" "\n[3] Proxy Type" "\n[4] Delay" @@ -10127,7 +10127,7 @@ def proxy_execute_announced( console.print( f"Name: {p_name}\n" f"Delay: {delay_}\n" - f"Spawner/Delegator: {spawner}\n" + f"Spawner/Delegatee: {spawner}\n" f"Proxy Type: {proxy_type}\n" f"Note: {note}\n" ) diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index 0d90bfdb6..fcb55aacd 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional import sys from async_substrate_interface.errors import StateDiscardedError @@ -55,50 +55,6 @@ class ProxyType(StrEnum): RootClaim = "RootClaim" -async def extract_event_attributes_from_receipt( - receipt: Any, event_name: str -) -> Optional[dict[str, Any]]: - """Extracts event attributes from a receipt, supporting nested and top-level event shapes.""" - - for event in await receipt.triggered_events: - if not isinstance(event, dict): - continue - event_data = event.get("event", event) - if not isinstance(event_data, dict): - continue - if event_data.get("event_id") == event_name: - attributes = event_data.get("attributes") - if isinstance(attributes, dict): - return attributes - return None - return None - - -def write_proxy_address_book_entry( - conn: Any, - cursor: Any, - *, - name: str, - ss58_address: str, - delay: int, - proxy_type: str, - note: str, - spawner: str, -) -> None: - """Writes a proxy address-book entry using the normalized storage schema.""" - - ProxyAddressBook.add_entry( - conn, - cursor, - name=name, - ss58_address=ss58_address, - delay=delay, - proxy_type=proxy_type, - note=note, - spawner=spawner, - ) - - # TODO add announce with also --reject and --remove @@ -214,70 +170,15 @@ async def create_proxy( ), ) if success: - attrs = await extract_event_attributes_from_receipt(receipt, "PureCreated") - if attrs is None: - msg = "Created pure proxy, but could not parse the `PureCreated` event from the receipt." - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - - created_pure = attrs.get("pure") - created_spawner = attrs.get("who") - created_proxy_type_name = attrs.get("proxy_type") - if not ( - isinstance(created_pure, str) - and isinstance(created_spawner, str) - and isinstance(created_proxy_type_name, str) - ): - msg = ( - "Created pure proxy, but received malformed `PureCreated` event attributes " - "from the receipt." - ) - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - - try: - created_proxy_type = ProxyType(created_proxy_type_name) - except ValueError: - msg = ( - "Created pure proxy, but received an unknown proxy type in the receipt: " - f"{created_proxy_type_name}." - ) - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - + created_pure = None + created_spawner = None + created_proxy_type = None + for event in await receipt.triggered_events: + if event["event_id"] == "PureCreated": + attrs = event["attributes"] + created_pure = attrs["pure"] + created_spawner = attrs["who"] + created_proxy_type = getattr(ProxyType, attrs["proxy_type"]) msg = ( f"Created pure '{created_pure}' " f"from spawner '{created_spawner}' " @@ -321,9 +222,9 @@ async def create_proxy( "[Optional] Add a note for this proxy", default="" ) with ProxyAddressBook.get_db() as (conn, cursor): - write_proxy_address_book_entry( - conn=conn, - cursor=cursor, + ProxyAddressBook.add_entry( + conn, + cursor, name=proxy_name, ss58_address=created_pure, delay=delay, @@ -497,70 +398,16 @@ async def add_proxy( era={"period": period}, ) if success: - attrs = await extract_event_attributes_from_receipt(receipt, "ProxyAdded") - if attrs is None: - msg = "Added proxy, but could not parse the `ProxyAdded` event from the receipt." - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - - delegatee = attrs.get("delegatee") - delegator = attrs.get("delegator") - created_proxy_type_name = attrs.get("proxy_type") - if not ( - isinstance(delegatee, str) - and isinstance(delegator, str) - and isinstance(created_proxy_type_name, str) - ): - msg = ( - "Added proxy, but received malformed `ProxyAdded` event attributes " - "from the receipt." - ) - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - - try: - created_proxy_type = ProxyType(created_proxy_type_name) - except ValueError: - msg = ( - "Added proxy, but received an unknown proxy type in the receipt: " - f"{created_proxy_type_name}." - ) - if json_output: - json_console.print_json( - data={ - "success": success, - "message": msg, - "data": None, - "extrinsic_identifier": await receipt.get_extrinsic_identifier(), - } - ) - else: - await print_extrinsic_id(receipt) - console.print(msg) - return None - + delegatee = None + delegator = None + created_proxy_type = None + for event in await receipt.triggered_events: + if event["event_id"] == "ProxyAdded": + attrs = event["attributes"] + delegatee = attrs["delegatee"] + delegator = attrs["delegator"] + created_proxy_type = getattr(ProxyType, attrs["proxy_type"]) + break msg = ( f"Added proxy delegatee '{delegatee}' " f"from delegator '{delegator}' " @@ -604,9 +451,9 @@ async def add_proxy( "[Optional] Add a note for this proxy", default="" ) with ProxyAddressBook.get_db() as (conn, cursor): - write_proxy_address_book_entry( - conn=conn, - cursor=cursor, + ProxyAddressBook.add_entry( + conn, + cursor, name=proxy_name, ss58_address=delegatee, delay=delay,