pezkuwi_subxt_metadata/from/
v14.rs1use super::TryFromError;
6
7use crate::{
8 utils::{ordered_map::OrderedMap, variant_index::VariantIndex},
9 ConstantMetadata, CustomMetadataInner, ExtrinsicMetadata, Metadata, OuterEnumsMetadata,
10 PalletMetadataInner, StorageEntryMetadata, StorageMetadata, TransactionExtensionMetadataInner,
11};
12use alloc::{borrow::ToOwned, collections::BTreeMap, format, string::String, vec, vec::Vec};
13use frame_decode::storage::StorageTypeInfo;
14use frame_metadata::v14;
15use hashbrown::HashMap;
16use scale_info::form::PortableForm;
17
18impl TryFrom<v14::RuntimeMetadataV14> for Metadata {
19 type Error = TryFromError;
20 fn try_from(mut m: v14::RuntimeMetadataV14) -> Result<Self, TryFromError> {
21 let outer_enums = generate_outer_enums(&mut m)?;
22 let missing_extrinsic_type_ids = MissingExtrinsicTypeIds::generate_from(&m)?;
23
24 let mut pallets = OrderedMap::new();
25 let mut pallets_by_index = HashMap::new();
26 for (pos, p) in m.pallets.iter().enumerate() {
27 let name: String = p.name.clone();
28
29 let storage = match &p.storage {
30 None => None,
31 Some(s) => Some(StorageMetadata {
32 prefix: s.prefix.clone(),
33 entries: s
34 .entries
35 .iter()
36 .map(|s| {
37 let entry_name: String = s.name.clone();
38 let storage_info = m
39 .storage_info(&name, &entry_name)
40 .map_err(|e| e.into_owned())?
41 .into_owned();
42 let storage_entry = StorageEntryMetadata {
43 name: entry_name.clone(),
44 info: storage_info,
45 docs: s.docs.clone(),
46 };
47
48 Ok::<_, TryFromError>((entry_name, storage_entry))
49 })
50 .collect::<Result<_, TryFromError>>()?,
51 }),
52 };
53
54 let constants = p.constants.iter().map(|c| {
55 let name = c.name.clone();
56 (name, from_constant_metadata(c.clone()))
57 });
58
59 let call_variant_index =
60 VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), &m.types);
61 let error_variant_index =
62 VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), &m.types);
63 let event_variant_index =
64 VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), &m.types);
65
66 pallets_by_index.insert(p.index, pos);
67 pallets.push_insert(
68 name.clone(),
69 PalletMetadataInner {
70 name: name.clone(),
71 call_index: p.index,
72 event_index: p.index,
73 error_index: p.index,
74 storage,
75 call_ty: p.calls.as_ref().map(|c| c.ty.id),
76 call_variant_index,
77 event_ty: p.event.as_ref().map(|e| e.ty.id),
78 event_variant_index,
79 error_ty: p.error.as_ref().map(|e| e.ty.id),
80 error_variant_index,
81 constants: constants.collect(),
82 view_functions: Default::default(),
83 associated_types: Default::default(),
84 docs: vec![],
85 },
86 );
87 }
88
89 let dispatch_error_ty = m
90 .types
91 .types
92 .iter()
93 .find(|ty| ty.ty.path.segments == ["sp_runtime", "DispatchError"])
94 .map(|ty| ty.id);
95
96 Ok(Metadata {
97 types: m.types,
98 pallets,
99 pallets_by_call_index: pallets_by_index.clone(),
100 pallets_by_error_index: pallets_by_index.clone(),
101 pallets_by_event_index: pallets_by_index,
102 extrinsic: from_extrinsic_metadata(m.extrinsic, missing_extrinsic_type_ids),
103 dispatch_error_ty,
104 outer_enums: OuterEnumsMetadata {
105 call_enum_ty: outer_enums.call_enum_ty.id,
106 event_enum_ty: outer_enums.event_enum_ty.id,
107 error_enum_ty: outer_enums.error_enum_ty.id,
108 },
109 apis: Default::default(),
110 custom: CustomMetadataInner { map: Default::default() },
111 })
112 }
113}
114
115fn from_signed_extension_metadata(
116 value: v14::SignedExtensionMetadata<PortableForm>,
117) -> TransactionExtensionMetadataInner {
118 TransactionExtensionMetadataInner {
119 identifier: value.identifier,
120 extra_ty: value.ty.id,
121 additional_ty: value.additional_signed.id,
122 }
123}
124
125fn from_extrinsic_metadata(
126 value: v14::ExtrinsicMetadata<PortableForm>,
127 missing_ids: MissingExtrinsicTypeIds,
128) -> ExtrinsicMetadata {
129 let transaction_extensions: Vec<_> = value
130 .signed_extensions
131 .into_iter()
132 .map(from_signed_extension_metadata)
133 .collect();
134
135 let transaction_extension_indexes = (0..transaction_extensions.len() as u32).collect();
136
137 ExtrinsicMetadata {
138 supported_versions: vec![value.version],
139 transaction_extensions,
140 address_ty: missing_ids.address,
141 signature_ty: missing_ids.signature,
142 transaction_extensions_by_version: BTreeMap::from_iter([(
143 0,
144 transaction_extension_indexes,
145 )]),
146 }
147}
148
149fn from_constant_metadata(s: v14::PalletConstantMetadata<PortableForm>) -> ConstantMetadata {
150 ConstantMetadata { name: s.name, ty: s.ty.id, value: s.value, docs: s.docs }
151}
152
153fn generate_outer_enums(
154 metadata: &mut v14::RuntimeMetadataV14,
155) -> Result<frame_metadata::v15::OuterEnums<scale_info::form::PortableForm>, TryFromError> {
156 let outer_enums = OuterEnums::find_in(&metadata.types);
157
158 let Some(call_enum_id) = outer_enums.call_ty else {
159 return Err(TryFromError::TypeNameNotFound("RuntimeCall".into()));
160 };
161 let Some(event_type_id) = outer_enums.event_ty else {
162 return Err(TryFromError::TypeNameNotFound("RuntimeEvent".into()));
163 };
164 let error_type_id = if let Some(id) = outer_enums.error_ty {
165 id
166 } else {
167 let call_enum = &metadata.types.types[call_enum_id as usize];
168 let mut error_path = call_enum.ty.path.segments.clone();
169
170 let Some(last) = error_path.last_mut() else {
171 return Err(TryFromError::InvalidTypePath("RuntimeCall".into()));
172 };
173 "RuntimeError".clone_into(last);
174 generate_outer_error_enum_type(metadata, error_path)
175 };
176
177 Ok(frame_metadata::v15::OuterEnums {
178 call_enum_ty: call_enum_id.into(),
179 event_enum_ty: event_type_id.into(),
180 error_enum_ty: error_type_id.into(),
181 })
182}
183
184fn generate_outer_error_enum_type(
188 metadata: &mut v14::RuntimeMetadataV14,
189 path_segments: Vec<String>,
190) -> u32 {
191 let variants: Vec<_> = metadata
192 .pallets
193 .iter()
194 .filter_map(|pallet| {
195 let error = pallet.error.as_ref()?;
196 let path = format!("{}Error", pallet.name);
197 let ty = error.ty.id.into();
198
199 Some(scale_info::Variant {
200 name: pallet.name.clone(),
201 fields: vec![scale_info::Field {
202 name: None,
203 ty,
204 type_name: Some(path),
205 docs: vec![],
206 }],
207 index: pallet.index,
208 docs: vec![],
209 })
210 })
211 .collect();
212
213 let enum_type = scale_info::Type {
214 path: scale_info::Path { segments: path_segments },
215 type_params: vec![],
216 type_def: scale_info::TypeDef::Variant(scale_info::TypeDefVariant { variants }),
217 docs: vec![],
218 };
219
220 let enum_type_id = metadata.types.types.len() as u32;
221
222 metadata
223 .types
224 .types
225 .push(scale_info::PortableType { id: enum_type_id, ty: enum_type });
226
227 enum_type_id
228}
229
230#[derive(Clone, Copy)]
234struct MissingExtrinsicTypeIds {
235 address: u32,
236 signature: u32,
237}
238
239impl MissingExtrinsicTypeIds {
240 fn generate_from(
241 metadata: &v14::RuntimeMetadataV14,
242 ) -> Result<MissingExtrinsicTypeIds, TryFromError> {
243 const ADDRESS: &str = "Address";
244 const SIGNATURE: &str = "Signature";
245
246 let extrinsic_id = metadata.extrinsic.ty.id;
247 let Some(extrinsic_ty) = metadata.types.resolve(extrinsic_id) else {
248 return Err(TryFromError::TypeNotFound(extrinsic_id));
249 };
250
251 let find_param = |name: &'static str| -> Option<u32> {
252 extrinsic_ty
253 .type_params
254 .iter()
255 .find(|param| param.name.as_str() == name)
256 .and_then(|param| param.ty.as_ref())
257 .map(|ty| ty.id)
258 };
259
260 let Some(address) = find_param(ADDRESS) else {
261 return Err(TryFromError::TypeNameNotFound(ADDRESS.into()));
262 };
263 let Some(signature) = find_param(SIGNATURE) else {
264 return Err(TryFromError::TypeNameNotFound(SIGNATURE.into()));
265 };
266
267 Ok(MissingExtrinsicTypeIds { address, signature })
268 }
269}
270
271pub struct OuterEnums {
273 pub call_ty: Option<u32>,
275 pub event_ty: Option<u32>,
277 pub error_ty: Option<u32>,
279}
280
281impl OuterEnums {
282 pub fn find_in(types: &scale_info::PortableRegistry) -> OuterEnums {
283 let find_type = |name: &str| {
284 types.types.iter().find_map(|ty| {
285 let ident = ty.ty.path.ident()?;
286
287 if ident != name {
288 return None;
289 }
290
291 let scale_info::TypeDef::Variant(_) = &ty.ty.type_def else {
292 return None;
293 };
294
295 Some(ty.id)
296 })
297 };
298
299 OuterEnums {
300 call_ty: find_type("RuntimeCall"),
301 event_ty: find_type("RuntimeEvent"),
302 error_ty: find_type("RuntimeError"),
303 }
304 }
305}