subxt_metadata/from_into/
v15.rs

1// Copyright 2019-2025 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use super::TryFromError;
6
7use crate::utils::variant_index::VariantIndex;
8use crate::{
9    utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata,
10    OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadata,
11    RuntimeApiMethodParamMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
12    StorageHasher, StorageMetadata, TransactionExtensionMetadata,
13};
14use alloc::borrow::ToOwned;
15use alloc::vec;
16use frame_metadata::v15;
17use hashbrown::HashMap;
18use scale_info::form::PortableForm;
19
20// Converting from V15 metadata into our Subxt repr.
21mod from_v15 {
22    use super::*;
23
24    impl TryFrom<v15::RuntimeMetadataV15> for Metadata {
25        type Error = TryFromError;
26        fn try_from(m: v15::RuntimeMetadataV15) -> Result<Self, TryFromError> {
27            let mut pallets = OrderedMap::new();
28            let mut pallets_by_index = HashMap::new();
29            for (pos, p) in m.pallets.into_iter().enumerate() {
30                let name: ArcStr = p.name.into();
31
32                let storage = p.storage.map(|s| StorageMetadata {
33                    prefix: s.prefix,
34                    entries: s
35                        .entries
36                        .into_iter()
37                        .map(|s| {
38                            let name: ArcStr = s.name.clone().into();
39                            (name.clone(), from_storage_entry_metadata(name, s))
40                        })
41                        .collect(),
42                });
43                let constants = p.constants.into_iter().map(|c| {
44                    let name: ArcStr = c.name.clone().into();
45                    (name.clone(), from_constant_metadata(name, c))
46                });
47
48                let call_variant_index =
49                    VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), &m.types);
50                let error_variant_index =
51                    VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), &m.types);
52                let event_variant_index =
53                    VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), &m.types);
54
55                pallets_by_index.insert(p.index, pos);
56                pallets.push_insert(
57                    name.clone(),
58                    PalletMetadataInner {
59                        name,
60                        index: p.index,
61                        storage,
62                        call_ty: p.calls.map(|c| c.ty.id),
63                        call_variant_index,
64                        event_ty: p.event.map(|e| e.ty.id),
65                        event_variant_index,
66                        error_ty: p.error.map(|e| e.ty.id),
67                        error_variant_index,
68                        constants: constants.collect(),
69                        docs: p.docs,
70                    },
71                );
72            }
73
74            let apis = m.apis.into_iter().map(|api| {
75                let name: ArcStr = api.name.clone().into();
76                (name.clone(), from_runtime_api_metadata(name, api))
77            });
78
79            let dispatch_error_ty = m
80                .types
81                .types
82                .iter()
83                .find(|ty| ty.ty.path.segments == ["sp_runtime", "DispatchError"])
84                .map(|ty| ty.id);
85
86            Ok(Metadata {
87                types: m.types,
88                pallets,
89                pallets_by_index,
90                extrinsic: from_extrinsic_metadata(m.extrinsic),
91                runtime_ty: m.ty.id,
92                dispatch_error_ty,
93                apis: apis.collect(),
94                outer_enums: OuterEnumsMetadata {
95                    call_enum_ty: m.outer_enums.call_enum_ty.id,
96                    event_enum_ty: m.outer_enums.event_enum_ty.id,
97                    error_enum_ty: m.outer_enums.error_enum_ty.id,
98                },
99                custom: m.custom,
100            })
101        }
102    }
103
104    fn from_signed_extension_metadata(
105        value: v15::SignedExtensionMetadata<PortableForm>,
106    ) -> TransactionExtensionMetadata {
107        TransactionExtensionMetadata {
108            identifier: value.identifier,
109            extra_ty: value.ty.id,
110            additional_ty: value.additional_signed.id,
111        }
112    }
113
114    fn from_extrinsic_metadata(value: v15::ExtrinsicMetadata<PortableForm>) -> ExtrinsicMetadata {
115        ExtrinsicMetadata {
116            supported_versions: vec![value.version],
117            transaction_extensions: value
118                .signed_extensions
119                .into_iter()
120                .map(from_signed_extension_metadata)
121                .collect(),
122            address_ty: value.address_ty.id,
123            call_ty: value.call_ty.id,
124            signature_ty: value.signature_ty.id,
125            extra_ty: value.extra_ty.id,
126            transaction_extensions_version: 0,
127        }
128    }
129
130    fn from_storage_hasher(value: v15::StorageHasher) -> StorageHasher {
131        match value {
132            v15::StorageHasher::Blake2_128 => StorageHasher::Blake2_128,
133            v15::StorageHasher::Blake2_256 => StorageHasher::Blake2_256,
134            v15::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat,
135            v15::StorageHasher::Twox128 => StorageHasher::Twox128,
136            v15::StorageHasher::Twox256 => StorageHasher::Twox256,
137            v15::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat,
138            v15::StorageHasher::Identity => StorageHasher::Identity,
139        }
140    }
141
142    fn from_storage_entry_type(value: v15::StorageEntryType<PortableForm>) -> StorageEntryType {
143        match value {
144            v15::StorageEntryType::Plain(ty) => StorageEntryType::Plain(ty.id),
145            v15::StorageEntryType::Map {
146                hashers,
147                key,
148                value,
149            } => StorageEntryType::Map {
150                hashers: hashers.into_iter().map(from_storage_hasher).collect(),
151                key_ty: key.id,
152                value_ty: value.id,
153            },
154        }
155    }
156
157    fn from_storage_entry_modifier(value: v15::StorageEntryModifier) -> StorageEntryModifier {
158        match value {
159            v15::StorageEntryModifier::Optional => StorageEntryModifier::Optional,
160            v15::StorageEntryModifier::Default => StorageEntryModifier::Default,
161        }
162    }
163
164    fn from_storage_entry_metadata(
165        name: ArcStr,
166        s: v15::StorageEntryMetadata<PortableForm>,
167    ) -> StorageEntryMetadata {
168        StorageEntryMetadata {
169            name,
170            modifier: from_storage_entry_modifier(s.modifier),
171            entry_type: from_storage_entry_type(s.ty),
172            default: s.default,
173            docs: s.docs,
174        }
175    }
176
177    fn from_constant_metadata(
178        name: ArcStr,
179        s: v15::PalletConstantMetadata<PortableForm>,
180    ) -> ConstantMetadata {
181        ConstantMetadata {
182            name,
183            ty: s.ty.id,
184            value: s.value,
185            docs: s.docs,
186        }
187    }
188
189    fn from_runtime_api_metadata(
190        name: ArcStr,
191        s: v15::RuntimeApiMetadata<PortableForm>,
192    ) -> RuntimeApiMetadataInner {
193        RuntimeApiMetadataInner {
194            name,
195            docs: s.docs,
196            methods: s
197                .methods
198                .into_iter()
199                .map(|m| {
200                    let name: ArcStr = m.name.clone().into();
201                    (name.clone(), from_runtime_api_method_metadata(name, m))
202                })
203                .collect(),
204        }
205    }
206
207    fn from_runtime_api_method_metadata(
208        name: ArcStr,
209        s: v15::RuntimeApiMethodMetadata<PortableForm>,
210    ) -> RuntimeApiMethodMetadata {
211        RuntimeApiMethodMetadata {
212            name,
213            inputs: s
214                .inputs
215                .into_iter()
216                .map(from_runtime_api_method_param_metadata)
217                .collect(),
218            output_ty: s.output.id,
219            docs: s.docs,
220        }
221    }
222
223    fn from_runtime_api_method_param_metadata(
224        s: v15::RuntimeApiMethodParamMetadata<PortableForm>,
225    ) -> RuntimeApiMethodParamMetadata {
226        RuntimeApiMethodParamMetadata {
227            name: s.name,
228            ty: s.ty.id,
229        }
230    }
231}
232
233// Converting from our metadata repr to V15 metadata.
234mod into_v15 {
235    use super::*;
236
237    impl From<Metadata> for v15::RuntimeMetadataV15 {
238        fn from(m: Metadata) -> Self {
239            let pallets = m.pallets.into_values().into_iter().map(|p| {
240                let storage = p.storage.map(|s| v15::PalletStorageMetadata {
241                    prefix: s.prefix,
242                    entries: s
243                        .entries
244                        .into_values()
245                        .into_iter()
246                        .map(from_storage_entry_metadata)
247                        .collect(),
248                });
249
250                v15::PalletMetadata {
251                    name: (*p.name).to_owned(),
252                    calls: p
253                        .call_ty
254                        .map(|id| v15::PalletCallMetadata { ty: id.into() }),
255                    event: p
256                        .event_ty
257                        .map(|id| v15::PalletEventMetadata { ty: id.into() }),
258                    error: p
259                        .error_ty
260                        .map(|id| v15::PalletErrorMetadata { ty: id.into() }),
261                    storage,
262                    constants: p
263                        .constants
264                        .into_values()
265                        .into_iter()
266                        .map(from_constant_metadata)
267                        .collect(),
268                    index: p.index,
269                    docs: p.docs,
270                }
271            });
272
273            v15::RuntimeMetadataV15 {
274                types: m.types,
275                pallets: pallets.collect(),
276                ty: m.runtime_ty.into(),
277                extrinsic: from_extrinsic_metadata(m.extrinsic),
278                apis: m
279                    .apis
280                    .into_values()
281                    .into_iter()
282                    .map(from_runtime_api_metadata)
283                    .collect(),
284                outer_enums: v15::OuterEnums {
285                    call_enum_ty: m.outer_enums.call_enum_ty.into(),
286                    event_enum_ty: m.outer_enums.event_enum_ty.into(),
287                    error_enum_ty: m.outer_enums.error_enum_ty.into(),
288                },
289                custom: m.custom,
290            }
291        }
292    }
293
294    fn from_runtime_api_metadata(
295        r: RuntimeApiMetadataInner,
296    ) -> v15::RuntimeApiMetadata<PortableForm> {
297        v15::RuntimeApiMetadata {
298            name: (*r.name).to_owned(),
299            methods: r
300                .methods
301                .into_values()
302                .into_iter()
303                .map(from_runtime_api_method_metadata)
304                .collect(),
305            docs: r.docs,
306        }
307    }
308
309    fn from_runtime_api_method_metadata(
310        m: RuntimeApiMethodMetadata,
311    ) -> v15::RuntimeApiMethodMetadata<PortableForm> {
312        v15::RuntimeApiMethodMetadata {
313            name: (*m.name).to_owned(),
314            inputs: m
315                .inputs
316                .into_iter()
317                .map(from_runtime_api_method_param_metadata)
318                .collect(),
319            output: m.output_ty.into(),
320            docs: m.docs,
321        }
322    }
323
324    fn from_runtime_api_method_param_metadata(
325        p: RuntimeApiMethodParamMetadata,
326    ) -> v15::RuntimeApiMethodParamMetadata<PortableForm> {
327        v15::RuntimeApiMethodParamMetadata {
328            name: p.name,
329            ty: p.ty.into(),
330        }
331    }
332
333    fn from_extrinsic_metadata(e: ExtrinsicMetadata) -> v15::ExtrinsicMetadata<PortableForm> {
334        v15::ExtrinsicMetadata {
335            // V16 and above metadata can have multiple supported extrinsic versions. We have to
336            // pick just one of these if converting back to V14/V15 metadata.
337            //
338            // - Picking the largest may mean that older tooling won't be compatible (it may only
339            //   check/support older version).
340            // - Picking the smallest may mean that newer tooling won't work, or newer methods won't
341            //   work.
342            //
343            // Either could make sense, but we keep the oldest to prioritize backward compat with
344            // older tooling.
345            version: *e
346                .supported_versions
347                .iter()
348                .min()
349                .expect("at least one extrinsic version expected"),
350            signed_extensions: e
351                .transaction_extensions
352                .into_iter()
353                .map(from_signed_extension_metadata)
354                .collect(),
355            address_ty: e.address_ty.into(),
356            call_ty: e.call_ty.into(),
357            signature_ty: e.signature_ty.into(),
358            extra_ty: e.extra_ty.into(),
359        }
360    }
361
362    fn from_signed_extension_metadata(
363        s: TransactionExtensionMetadata,
364    ) -> v15::SignedExtensionMetadata<PortableForm> {
365        v15::SignedExtensionMetadata {
366            identifier: s.identifier,
367            ty: s.extra_ty.into(),
368            additional_signed: s.additional_ty.into(),
369        }
370    }
371
372    fn from_constant_metadata(c: ConstantMetadata) -> v15::PalletConstantMetadata<PortableForm> {
373        v15::PalletConstantMetadata {
374            name: (*c.name).to_owned(),
375            ty: c.ty.into(),
376            value: c.value,
377            docs: c.docs,
378        }
379    }
380
381    fn from_storage_entry_metadata(
382        s: StorageEntryMetadata,
383    ) -> v15::StorageEntryMetadata<PortableForm> {
384        v15::StorageEntryMetadata {
385            docs: s.docs,
386            default: s.default,
387            name: (*s.name).to_owned(),
388            ty: from_storage_entry_type(s.entry_type),
389            modifier: from_storage_entry_modifier(s.modifier),
390        }
391    }
392
393    fn from_storage_entry_modifier(s: StorageEntryModifier) -> v15::StorageEntryModifier {
394        match s {
395            StorageEntryModifier::Default => v15::StorageEntryModifier::Default,
396            StorageEntryModifier::Optional => v15::StorageEntryModifier::Optional,
397        }
398    }
399
400    fn from_storage_entry_type(s: StorageEntryType) -> v15::StorageEntryType<PortableForm> {
401        match s {
402            StorageEntryType::Plain(ty) => v15::StorageEntryType::Plain(ty.into()),
403            StorageEntryType::Map {
404                hashers,
405                key_ty,
406                value_ty,
407            } => v15::StorageEntryType::Map {
408                hashers: hashers.into_iter().map(from_storage_hasher).collect(),
409                key: key_ty.into(),
410                value: value_ty.into(),
411            },
412        }
413    }
414
415    fn from_storage_hasher(s: StorageHasher) -> v15::StorageHasher {
416        match s {
417            StorageHasher::Blake2_128 => v15::StorageHasher::Blake2_128,
418            StorageHasher::Blake2_256 => v15::StorageHasher::Blake2_256,
419            StorageHasher::Blake2_128Concat => v15::StorageHasher::Blake2_128Concat,
420            StorageHasher::Twox128 => v15::StorageHasher::Twox128,
421            StorageHasher::Twox256 => v15::StorageHasher::Twox256,
422            StorageHasher::Twox64Concat => v15::StorageHasher::Twox64Concat,
423            StorageHasher::Identity => v15::StorageHasher::Identity,
424        }
425    }
426}