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
@@ -120,24 +120,35 @@ def concat_hash_len(key_hasher: str) -> int:
120120 )
121121 middl_index = len (all_decoded ) // 2
122122 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 ):
123+ decoded_values = all_decoded [middl_index :]
124+ for (kts , vts ), (dk , dv ) in zip (
125+ zip (pre_decoded_key_types , pre_decoded_value_types ),
126+ zip (decoded_keys , decoded_values ),
127+ ):
125128 try :
126129 # strip key_hashers to use as item key
127130 if len (param_types ) - len (params ) == 1 :
128131 item_key = dk [1 ]
132+ if kts [kts .index (", " ) + 2 : kts .index (")" )] == "scale_info::0" :
133+ item_key = ss58_encode (bytes (item_key [0 ]), runtime .ss58_format )
134+
129135 else :
130136 item_key = tuple (
131137 dk [key + 1 ] for key in range (len (params ), len (param_types ) + 1 , 2 )
132138 )
139+ # TODO handle decoding here, but first figure out what triggers this
133140
134141 except Exception as _ :
135142 if not ignore_decoding_errors :
136143 raise
137144 item_key = None
138-
139- item_value = dv
140- result .append ([item_key , item_value ])
145+ try :
146+ value_type_str_int = int (vts .split ("::" )[1 ])
147+ decoded_type_str = runtime .type_id_to_name [value_type_str_int ]
148+ item_value = convert_account_ids (dv , decoded_type_str , runtime .ss58_format )
149+ except (ValueError , KeyError ) as e :
150+ item_value = dv
151+ result .append ([item_key , ScaleObj (item_value )])
141152 return result
142153
143154
@@ -154,3 +165,68 @@ def legacy_scale_decode(
154165 obj .decode (check_remaining = runtime .config .get ("strict_scale_decode" ))
155166
156167 return obj .value
168+
169+
170+ def is_accountid32 (value : Any ) -> bool :
171+ return (
172+ isinstance (value , tuple )
173+ and len (value ) == 32
174+ and all (isinstance (b , int ) and 0 <= b <= 255 for b in value )
175+ )
176+
177+
178+ def convert_account_ids (value : Any , type_str : str , ss58_format = 42 ) -> Any :
179+ if "AccountId32" not in type_str :
180+ return value
181+
182+ # Option<T>
183+ if type_str .startswith ("Option<" ) and value is not None :
184+ inner_type = type_str [7 :- 1 ]
185+ return convert_account_ids (value , inner_type )
186+ # Vec<T>
187+ if type_str .startswith ("Vec<" ) and isinstance (value , (list , tuple )):
188+ inner_type = type_str [4 :- 1 ]
189+ return tuple (convert_account_ids (v , inner_type ) for v in value )
190+
191+ # Vec<Vec<T>>
192+ if type_str .startswith ("Vec<Vec<" ) and isinstance (value , (list , tuple )):
193+ inner_type = type_str [8 :- 2 ]
194+ return tuple (
195+ tuple (convert_account_ids (v2 , inner_type ) for v2 in v1 ) for v1 in value
196+ )
197+
198+ # Tuple
199+ if type_str .startswith ("(" ) and isinstance (value , (list , tuple )):
200+ inner_parts = split_tuple_type (type_str )
201+ return tuple (convert_account_ids (v , t ) for v , t in zip (value , inner_parts ))
202+
203+ # AccountId32
204+ if type_str == "AccountId32" and is_accountid32 (value [0 ]):
205+ return ss58_encode (bytes (value [0 ]), ss58_format = ss58_format )
206+
207+ # Fallback
208+ return value
209+
210+
211+ def split_tuple_type (type_str : str ) -> list [str ]:
212+ """
213+ Splits a type string like '(AccountId32, Vec<StakeInfo>)' into ['AccountId32', 'Vec<StakeInfo>']
214+ Handles nested generics.
215+ """
216+ s = type_str [1 :- 1 ]
217+ parts = []
218+ depth = 0
219+ current = ""
220+ for char in s :
221+ if char == "," and depth == 0 :
222+ parts .append (current .strip ())
223+ current = ""
224+ else :
225+ if char == "<" :
226+ depth += 1
227+ elif char == ">" :
228+ depth -= 1
229+ current += char
230+ if current :
231+ parts .append (current .strip ())
232+ return parts
0 commit comments