@@ -1243,6 +1243,12 @@ def __init__(self):
12431243 "execute" ,
12441244 rich_help_panel = HELP_PANELS ["PROXY" ]["MGMT" ],
12451245 )(self .proxy_execute_announced )
1246+ self .proxy_app .command ("list" , rich_help_panel = HELP_PANELS ["PROXY" ]["MGMT" ])(
1247+ self .proxy_list
1248+ )
1249+ self .proxy_app .command ("reject" , rich_help_panel = HELP_PANELS ["PROXY" ]["MGMT" ])(
1250+ self .proxy_reject
1251+ )
12461252
12471253 # Sub command aliases
12481254 # Wallet
@@ -9892,6 +9898,7 @@ def proxy_remove(
98929898 delegate = is_valid_ss58_address_param (delegate )
98939899
98949900 self .verbosity_handler (quiet , verbose , json_output , prompt )
9901+
98959902 wallet = self .wallet_ask (
98969903 wallet_name = wallet_name ,
98979904 wallet_path = wallet_path ,
@@ -10226,6 +10233,281 @@ def proxy_execute_announced(
1022610233 with ProxyAnnouncements .get_db () as (conn , cursor ):
1022710234 ProxyAnnouncements .mark_as_executed (conn , cursor , got_call_from_db )
1022810235
10236+ def proxy_list (
10237+ self ,
10238+ address : Annotated [
10239+ Optional [str ],
10240+ typer .Option (
10241+ callback = is_valid_ss58_address_param ,
10242+ help = "The SS58 address to list proxies for. If not provided, uses the wallet's coldkey." ,
10243+ ),
10244+ ] = None ,
10245+ network : Optional [list [str ]] = Options .network ,
10246+ wallet_name : str = Options .wallet_name ,
10247+ wallet_path : str = Options .wallet_path ,
10248+ wallet_hotkey : str = Options .wallet_hotkey ,
10249+ quiet : bool = Options .quiet ,
10250+ verbose : bool = Options .verbose ,
10251+ json_output : bool = Options .json_output ,
10252+ ):
10253+ """
10254+ Lists all proxies for an account.
10255+
10256+ Queries the chain to display all proxy delegates configured for the specified address,
10257+ including their proxy types and delay settings.
10258+
10259+ [bold]Common Examples:[/bold]
10260+ 1. List proxies for your wallet
10261+ [green]$[/green] btcli proxy list
10262+
10263+ 2. List proxies for a specific address
10264+ [green]$[/green] btcli proxy list --address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
10265+
10266+ """
10267+ self .verbosity_handler (quiet , verbose , json_output , prompt = False )
10268+
10269+ # If no address provided, use wallet's coldkey
10270+ if address is None :
10271+ wallet = self .wallet_ask (
10272+ wallet_name = wallet_name ,
10273+ wallet_path = wallet_path ,
10274+ wallet_hotkey = wallet_hotkey ,
10275+ ask_for = [WO .NAME , WO .PATH ],
10276+ validate = WV .WALLET ,
10277+ )
10278+ address = wallet .coldkeypub .ss58_address
10279+
10280+ logger .debug (f"args:\n address: { address } \n network: { network } \n " )
10281+
10282+ return self ._run_command (
10283+ proxy_commands .list_proxies (
10284+ subtensor = self .initialize_chain (network ),
10285+ address = address ,
10286+ json_output = json_output ,
10287+ )
10288+ )
10289+
10290+ def proxy_reject (
10291+ self ,
10292+ delegate : Annotated [
10293+ Optional [str ],
10294+ typer .Option (
10295+ callback = is_valid_ss58_address_param ,
10296+ help = "The SS58 address of the delegate (proxy) who made the announcement." ,
10297+ ),
10298+ ] = None ,
10299+ call_hash : Annotated [
10300+ Optional [str ],
10301+ typer .Option (
10302+ help = "The hash of the announced call to reject" ,
10303+ ),
10304+ ] = None ,
10305+ network : Optional [list [str ]] = Options .network ,
10306+ wallet_name : str = Options .wallet_name ,
10307+ wallet_path : str = Options .wallet_path ,
10308+ wallet_hotkey : str = Options .wallet_hotkey ,
10309+ prompt : bool = Options .prompt ,
10310+ decline : bool = Options .decline ,
10311+ wait_for_inclusion : bool = Options .wait_for_inclusion ,
10312+ wait_for_finalization : bool = Options .wait_for_finalization ,
10313+ period : int = Options .period ,
10314+ quiet : bool = Options .quiet ,
10315+ verbose : bool = Options .verbose ,
10316+ json_output : bool = Options .json_output ,
10317+ ):
10318+ """
10319+ Rejects an announced proxy call.
10320+
10321+ Removes a previously announced call from the pending announcements, preventing it
10322+ from being executed. This must be called by the real account (the account that
10323+ granted the proxy permissions).
10324+
10325+ [bold]Common Examples:[/bold]
10326+ 1. Reject an announced call
10327+ [green]$[/green] btcli proxy reject --delegate 5GDel... --call-hash 0x1234...
10328+
10329+ """
10330+ self .verbosity_handler (quiet , verbose , json_output , prompt , decline )
10331+
10332+ logger .debug (
10333+ "args:\n "
10334+ f"delegate: { delegate } \n "
10335+ f"call_hash: { call_hash } \n "
10336+ f"network: { network } \n "
10337+ f"wait_for_finalization: { wait_for_finalization } \n "
10338+ f"wait_for_inclusion: { wait_for_inclusion } \n "
10339+ f"era: { period } \n "
10340+ )
10341+
10342+ wallet = self .wallet_ask (
10343+ wallet_name = wallet_name ,
10344+ wallet_path = wallet_path ,
10345+ wallet_hotkey = wallet_hotkey ,
10346+ ask_for = [WO .NAME , WO .PATH ],
10347+ validate = WV .WALLET ,
10348+ )
10349+
10350+ if not delegate :
10351+ if prompt :
10352+ delegate = Prompt .ask (
10353+ "Enter the SS58 address of the delegate (proxy) who made the announcement"
10354+ )
10355+ if not is_valid_ss58_address (delegate ):
10356+ print_error (f"Invalid SS58 address: { delegate } " )
10357+ return
10358+ else :
10359+ if json_output :
10360+ json_console .print_json (
10361+ data = {
10362+ "success" : False ,
10363+ "message" : "--delegate is required. Provide the SS58 address of the proxy that made the announcement." ,
10364+ "extrinsic_identifier" : None ,
10365+ }
10366+ )
10367+ else :
10368+ print_error (
10369+ "--delegate is required. Provide the SS58 address of the proxy that made the announcement."
10370+ )
10371+ return
10372+
10373+ # Try to find the announcement in the local DB
10374+ # DB stores address = the real account (the wallet calling reject)
10375+ real_address = wallet .coldkeypub .ss58_address
10376+ got_call_from_db : Optional [int ] = None
10377+ with ProxyAnnouncements .get_db () as (conn , cursor ):
10378+ announcements = ProxyAnnouncements .read_rows (conn , cursor )
10379+
10380+ if not call_hash :
10381+ potential_call_matches = []
10382+ for row in announcements :
10383+ (
10384+ id_ ,
10385+ address ,
10386+ epoch_time ,
10387+ block_ ,
10388+ call_hash_ ,
10389+ call_hex_ ,
10390+ call_serialized ,
10391+ executed_int ,
10392+ ) = row
10393+ executed = bool (executed_int )
10394+ if address == real_address and executed is False :
10395+ potential_call_matches .append (row )
10396+
10397+ if len (potential_call_matches ) == 0 :
10398+ if not prompt :
10399+ if json_output :
10400+ json_console .print_json (
10401+ data = {
10402+ "success" : False ,
10403+ "message" : "No pending announcements found in the local address book. Please provide --call-hash explicitly." ,
10404+ "extrinsic_identifier" : None ,
10405+ }
10406+ )
10407+ else :
10408+ print_error (
10409+ "No pending announcements found in the local address book. "
10410+ "Please provide --call-hash explicitly."
10411+ )
10412+ return
10413+ call_hash = Prompt .ask (
10414+ "Enter the call hash of the announcement to reject"
10415+ )
10416+ elif len (potential_call_matches ) == 1 :
10417+ call_hash = potential_call_matches [0 ][4 ]
10418+ got_call_from_db = potential_call_matches [0 ][0 ]
10419+ if not json_output :
10420+ console .print (f"Found announcement with call hash: { call_hash } " )
10421+ else :
10422+ if not prompt :
10423+ if json_output :
10424+ json_console .print_json (
10425+ data = {
10426+ "success" : False ,
10427+ "message" : "Multiple pending announcements found. Please provide --call-hash explicitly." ,
10428+ "extrinsic_identifier" : None ,
10429+ }
10430+ )
10431+ else :
10432+ print_error (
10433+ "Multiple pending announcements found. "
10434+ f"Please run without { arg__ ('--no-prompt' )} to select one, or provide --call-hash explicitly."
10435+ )
10436+ return
10437+ else :
10438+ console .print (
10439+ f"Found { len (potential_call_matches )} pending announcements. "
10440+ f"Please select the one to reject:"
10441+ )
10442+ for row in potential_call_matches :
10443+ (
10444+ id_ ,
10445+ address ,
10446+ epoch_time ,
10447+ block_ ,
10448+ call_hash_ ,
10449+ call_hex_ ,
10450+ call_serialized ,
10451+ executed_int ,
10452+ ) = row
10453+ console .print (
10454+ f"Time: { datetime .datetime .fromtimestamp (epoch_time )} \n "
10455+ f"Call Hash: { call_hash_ } \n Call:\n "
10456+ )
10457+ console .print_json (call_serialized )
10458+ if confirm_action (
10459+ "Is this the announcement to reject?" ,
10460+ decline = decline ,
10461+ quiet = quiet ,
10462+ ):
10463+ call_hash = call_hash_
10464+ got_call_from_db = id_
10465+ break
10466+ if call_hash is None :
10467+ print_error ("No announcement selected." )
10468+ return
10469+ else :
10470+ # call_hash provided, try to find it in DB
10471+ for row in announcements :
10472+ (
10473+ id_ ,
10474+ address ,
10475+ epoch_time ,
10476+ block_ ,
10477+ call_hash_ ,
10478+ call_hex_ ,
10479+ call_serialized ,
10480+ executed_int ,
10481+ ) = row
10482+ executed = bool (executed_int )
10483+ if (
10484+ (call_hash_ == call_hash or f"0x{ call_hash_ } " == call_hash )
10485+ and address == real_address
10486+ and executed is False
10487+ ):
10488+ got_call_from_db = id_
10489+ break
10490+
10491+ success = self ._run_command (
10492+ proxy_commands .reject_announcement (
10493+ subtensor = self .initialize_chain (network ),
10494+ wallet = wallet ,
10495+ delegate = delegate ,
10496+ call_hash = call_hash ,
10497+ prompt = prompt ,
10498+ decline = decline ,
10499+ quiet = quiet ,
10500+ wait_for_inclusion = wait_for_inclusion ,
10501+ wait_for_finalization = wait_for_finalization ,
10502+ period = period ,
10503+ json_output = json_output ,
10504+ )
10505+ )
10506+
10507+ if success and got_call_from_db is not None :
10508+ with ProxyAnnouncements .get_db () as (conn , cursor ):
10509+ ProxyAnnouncements .mark_as_executed (conn , cursor , got_call_from_db )
10510+
1022910511 @staticmethod
1023010512 def convert (
1023110513 from_rao : Optional [str ] = typer .Option (
0 commit comments