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