1- from typing import Union , TYPE_CHECKING
1+ from typing import Union , TYPE_CHECKING , Any
22
33from bt_decode import AxonInfo , PrometheusInfo , decode_list
4- from scalecodec import ScaleBytes
4+ from scalecodec import ScaleBytes , ss58_encode
55
66from async_substrate_interface .utils import hex_to_bytes
77from async_substrate_interface .types import ScaleObj
@@ -81,6 +81,7 @@ def decode_query_map(
8181 value_type ,
8282 key_hashers ,
8383 ignore_decoding_errors ,
84+ decode_ss58 : bool = False ,
8485):
8586 def concat_hash_len (key_hasher : str ) -> int :
8687 """
@@ -120,12 +121,21 @@ def concat_hash_len(key_hasher: str) -> int:
120121 )
121122 middl_index = len (all_decoded ) // 2
122123 decoded_keys = all_decoded [:middl_index ]
123- decoded_values = [ScaleObj (x ) for x in all_decoded [middl_index :]]
124- for dk , dv in zip (decoded_keys , decoded_values ):
124+ decoded_values = all_decoded [middl_index :]
125+ for kts , vts , dk , dv in zip (
126+ pre_decoded_key_types ,
127+ pre_decoded_value_types ,
128+ decoded_keys ,
129+ decoded_values ,
130+ ):
125131 try :
126132 # strip key_hashers to use as item key
127133 if len (param_types ) - len (params ) == 1 :
128134 item_key = dk [1 ]
135+ if decode_ss58 :
136+ if kts [kts .index (", " ) + 2 : kts .index (")" )] == "scale_info::0" :
137+ item_key = ss58_encode (bytes (item_key [0 ]), runtime .ss58_format )
138+
129139 else :
130140 item_key = tuple (
131141 dk [key + 1 ] for key in range (len (params ), len (param_types ) + 1 , 2 )
@@ -135,9 +145,17 @@ def concat_hash_len(key_hasher: str) -> int:
135145 if not ignore_decoding_errors :
136146 raise
137147 item_key = None
138-
139148 item_value = dv
140- result .append ([item_key , item_value ])
149+ if decode_ss58 :
150+ try :
151+ value_type_str_int = int (vts .split ("::" )[1 ])
152+ decoded_type_str = runtime .type_id_to_name [value_type_str_int ]
153+ item_value = convert_account_ids (
154+ dv , decoded_type_str , runtime .ss58_format
155+ )
156+ except (ValueError , KeyError ):
157+ pass
158+ result .append ([item_key , ScaleObj (item_value )])
141159 return result
142160
143161
@@ -154,3 +172,68 @@ def legacy_scale_decode(
154172 obj .decode (check_remaining = runtime .config .get ("strict_scale_decode" ))
155173
156174 return obj .value
175+
176+
177+ def is_accountid32 (value : Any ) -> bool :
178+ return (
179+ isinstance (value , tuple )
180+ and len (value ) == 32
181+ and all (isinstance (b , int ) and 0 <= b <= 255 for b in value )
182+ )
183+
184+
185+ def convert_account_ids (value : Any , type_str : str , ss58_format = 42 ) -> Any :
186+ if "AccountId32" not in type_str :
187+ return value
188+
189+ # Option<T>
190+ if type_str .startswith ("Option<" ) and value is not None :
191+ inner_type = type_str [7 :- 1 ]
192+ return convert_account_ids (value , inner_type )
193+ # Vec<T>
194+ if type_str .startswith ("Vec<" ) and isinstance (value , (list , tuple )):
195+ inner_type = type_str [4 :- 1 ]
196+ return tuple (convert_account_ids (v , inner_type ) for v in value )
197+
198+ # Vec<Vec<T>>
199+ if type_str .startswith ("Vec<Vec<" ) and isinstance (value , (list , tuple )):
200+ inner_type = type_str [8 :- 2 ]
201+ return tuple (
202+ tuple (convert_account_ids (v2 , inner_type ) for v2 in v1 ) for v1 in value
203+ )
204+
205+ # Tuple
206+ if type_str .startswith ("(" ) and isinstance (value , (list , tuple )):
207+ inner_parts = split_tuple_type (type_str )
208+ return tuple (convert_account_ids (v , t ) for v , t in zip (value , inner_parts ))
209+
210+ # AccountId32
211+ if type_str == "AccountId32" and is_accountid32 (value [0 ]):
212+ return ss58_encode (bytes (value [0 ]), ss58_format = ss58_format )
213+
214+ # Fallback
215+ return value
216+
217+
218+ def split_tuple_type (type_str : str ) -> list [str ]:
219+ """
220+ Splits a type string like '(AccountId32, Vec<StakeInfo>)' into ['AccountId32', 'Vec<StakeInfo>']
221+ Handles nested generics.
222+ """
223+ s = type_str [1 :- 1 ]
224+ parts = []
225+ depth = 0
226+ current = ""
227+ for char in s :
228+ if char == "," and depth == 0 :
229+ parts .append (current .strip ())
230+ current = ""
231+ else :
232+ if char == "<" :
233+ depth += 1
234+ elif char == ">" :
235+ depth -= 1
236+ current += char
237+ if current :
238+ parts .append (current .strip ())
239+ return parts
0 commit comments