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