frame_decode/
lib.rs

1// Copyright (C) 2022-2025 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Decode extrinsics and storage values from substrate based networks which expose `frame-metadata::RuntimeMetadata`
17//! like Polkadot.
18//!
19//! - See [`extrinsics`] for decoding Extrinsics.
20//! - See [`storage`] for encoding/decoding storage keys and decoding values.
21//! - See [`runtime_apis`] for encoding Runtime API inputs and decoding Runtime API responses
22//! - See [`legacy_types`] to access historic type information for certain chains.
23//!
24#![deny(missing_docs)]
25#![cfg_attr(not(feature = "std"), no_std)]
26
27extern crate alloc;
28
29mod methods;
30mod utils;
31
32pub mod extrinsics {
33    //! This module contains functions for decoding extrinsics.
34    //!
35    //! - See [`decode_extrinsic`] for a general function to decode modern or historic extrinsics.
36    //! - See [`ExtrinsicTypeInfo`] for the underlying trait which extracts the relevant information.
37
38    pub use crate::methods::extrinsic_decoder::{
39        Extrinsic, ExtrinsicDecodeError, ExtrinsicExtensions, ExtrinsicOwned, ExtrinsicSignature,
40        ExtrinsicType, NamedArg, decode_extrinsic,
41    };
42    pub use crate::methods::extrinsic_type_info::{
43        ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError,
44        ExtrinsicSignatureInfo, ExtrinsicTypeInfo,
45    };
46}
47
48pub mod storage {
49    //! This module contains functions for decoding storage keys and values.
50    //!
51    //! - See [`decode_storage_key`] and [`decode_storage_value`] to decode storage keys or values
52    //!   from modern or historic runtimes.
53    //! - See [`encode_storage_key_prefix`] to encode storage prefixes, and [`encode_storage_key`] to encode
54    //!   storage keys.
55    //! - See [`StorageTypeInfo`] for the underlying trait which provides storage entry information.
56    //! - See [`StorageEntryInfo`] for a underlying trait which provides information about the available
57    //!   storage entries.
58
59    pub use crate::methods::storage_decoder::{
60        StorageKey, StorageKeyDecodeError, StorageKeyPart, StorageKeyPartValue,
61        StorageKeyValueDecodeError, StorageValueDecodeError,
62        decode_default_storage_value_with_info, decode_storage_key, decode_storage_key_values,
63        decode_storage_key_with_info, decode_storage_value, decode_storage_value_with_info,
64    };
65    pub use crate::methods::storage_encoder::{
66        StorageKeyEncodeError, encode_storage_key, encode_storage_key_prefix,
67        encode_storage_key_suffix, encode_storage_key_suffix_to,
68        encode_storage_key_suffix_with_info_to, encode_storage_key_to,
69        encode_storage_key_with_info, encode_storage_key_with_info_to,
70    };
71    pub use crate::methods::storage_type_info::{
72        StorageEntry, StorageEntryInfo, StorageHasher, StorageInfo, StorageInfoError,
73        StorageKeyInfo, StorageTypeInfo,
74    };
75    pub use crate::utils::{
76        DecodableValues, EncodableValues, IntoDecodableValues, IntoEncodableValues,
77    };
78}
79
80pub mod constants {
81    //! This module contains types and functions for working with constants.
82    //!
83    //! - See [`decode_constant`] and [`decode_constant_with_info`] to decode constants
84    //! - See [`ConstantTypeInfo`] for the underlying trait which extracts constant
85    //!   information from metadata.
86    //! - See [`ConstantEntryInfo`] for a underlying trait which provides information about the available
87    //!   constants.
88
89    pub use crate::methods::constant_decoder::{
90        ConstantDecodeError, decode_constant, decode_constant_with_info,
91    };
92    pub use crate::methods::constant_type_info::{
93        ConstantEntry, ConstantEntryInfo, ConstantInfo, ConstantInfoError, ConstantTypeInfo,
94    };
95}
96
97pub mod runtime_apis {
98    //! This module contains types and functions for working with Runtime APIs.
99    //!
100    //! - See [`encode_runtime_api_name`] and [`encode_runtime_api_inputs`] to encode
101    //!   the name and inputs to make a Runtime API call.
102    //! - See [`decode_runtime_api_response`] to decode Runtime API responses.
103    //! - See [`RuntimeApiTypeInfo`] for the underlying trait which extracts the relevant information.
104    //! - See [`RuntimeApiEntryInfo`] for a underlying trait which provides information about the available
105    //!   Runtime APIs.
106
107    pub use crate::methods::runtime_api_decoder::{
108        RuntimeApiDecodeError, decode_runtime_api_response, decode_runtime_api_response_with_info,
109    };
110    pub use crate::methods::runtime_api_encoder::{
111        RuntimeApiInputsEncodeError, encode_runtime_api_inputs, encode_runtime_api_inputs_to,
112        encode_runtime_api_inputs_with_info_to, encode_runtime_api_name,
113    };
114    pub use crate::methods::runtime_api_type_info::{
115        RuntimeApiEntry, RuntimeApiEntryInfo, RuntimeApiInfo, RuntimeApiInfoError, RuntimeApiInput,
116        RuntimeApiTypeInfo,
117    };
118    pub use crate::utils::{EncodableValues, IntoEncodableValues};
119}
120
121pub mod view_functions {
122    //! This module contains types and functions for working with View Functions.
123    //!
124    //! - See [`RUNTIME_API_NAME`] and [`encode_view_function_inputs`] to obtain the Runtime API name
125    //!   and the encoded input data required to call a given View Function.
126    //! - See [`decode_view_function_response`] to decode View Function responses.
127    //! - See [`ViewFunctionTypeInfo`] for the underlying trait which extracts the relevant information.
128    //! - See [`ViewFunctionEntryInfo`] for a underlying trait which provides information about the available
129    //!   View Functions.
130
131    pub use crate::methods::view_function_decoder::{
132        ViewFunctionDecodeError, decode_view_function_response,
133        decode_view_function_response_with_info,
134    };
135    pub use crate::methods::view_function_encoder::{
136        RUNTIME_API_NAME, ViewFunctionInputsEncodeError, encode_view_function_inputs,
137        encode_view_function_inputs_to, encode_view_function_inputs_with_info_to,
138    };
139    pub use crate::methods::view_function_type_info::{
140        ViewFunctionEntry, ViewFunctionEntryInfo, ViewFunctionInfo, ViewFunctionInfoError,
141        ViewFunctionInput, ViewFunctionTypeInfo,
142    };
143    pub use crate::utils::{EncodableValues, IntoEncodableValues};
144}
145
146pub mod custom_values {
147    //! This module contains types and functions for working with custom values.
148    //!
149    //! - See [`decode_custom_value`] and [`decode_custom_value_with_info`] to decode custom values
150    //! - See [`CustomValueTypeInfo`] for the underlying trait which extracts custom value
151    //!   information from metadata.
152    //! - See [`CustomValueEntryInfo`] for a underlying trait which provides information about the available
153    //!   custom values.
154
155    pub use crate::methods::custom_value_decoder::{
156        CustomValueDecodeError, decode_custom_value, decode_custom_value_with_info,
157    };
158    pub use crate::methods::custom_value_type_info::{
159        CustomValue, CustomValueEntryInfo, CustomValueInfo, CustomValueInfoError,
160        CustomValueTypeInfo,
161    };
162}
163
164#[cfg(feature = "legacy-types")]
165pub mod legacy_types {
166    //! This module contains legacy types that can be used to decode pre-V14 blocks and storage.
167
168    pub mod polkadot {
169        //! Legacy types for Polkadot chains.
170
171        /// Legacy types for the Polkadot Relay Chain.
172        pub fn relay_chain() -> scale_info_legacy::ChainTypeRegistry {
173            // This is a convenience function to load the Polkadot relay chain types.
174            // It is used in the examples in this crate.
175            let bytes = include_bytes!("../types/polkadot_relay_types.yaml");
176            serde_yaml::from_slice(bytes).expect("Polkadot RC types are valid YAML")
177        }
178    }
179
180    // Hidden until the types are ready.
181    #[doc(hidden)]
182    pub mod kusama {
183        //! Legacy types for Kusama chains.
184
185        /// Legacy types for the Kusama Relay Chain.
186        pub fn relay_chain() -> scale_info_legacy::ChainTypeRegistry {
187            // This is a convenience function to load the Polkadot relay chain types.
188            // It is used in the examples in this crate.
189            let bytes = include_bytes!("../types/kusama_relay_types.yaml");
190            serde_yaml::from_slice(bytes).expect("Kusama RC types are valid YAML")
191        }
192
193        /// Legacy types for the Kusama Asset Hub.
194        pub fn asset_hub() -> scale_info_legacy::ChainTypeRegistry {
195            // This is a convenience function to load the Polkadot relay chain types.
196            // It is used in the examples in this crate.
197            let bytes = include_bytes!("../types/kusama_assethub_types.yaml");
198            serde_yaml::from_slice(bytes).expect("Kusama AssetHub types are valid YAML")
199        }
200    }
201}
202
203pub mod helpers {
204    //! Helper functions and types to assist with decoding.
205    //!
206    //! - [`type_registry_from_metadata`] is expected to be used when decoding things from historic
207    //!   runtimes, adding the ability to decode some types from information in the metadata.
208    //! - [`decode_with_error_tracing`] is like [`decode_with_visitor`], but
209    //!   will use a tracing visitor (if the `error-tracing` feature is enabled) to provide more
210    //!   information in the event that decoding fails.
211
212    pub use crate::methods::Entry;
213
214    pub use crate::utils::{
215        DecodableValues, DecodeErrorTrace, EncodableValues, IntoDecodableValues,
216        IntoEncodableValues, decode_with_error_tracing,
217    };
218    #[cfg(feature = "legacy")]
219    pub use crate::utils::{type_registry_from_metadata, type_registry_from_metadata_any};
220
221    /// An alias to [`scale_decode::visitor::decode_with_visitor`]. This can be used to decode the byte ranges
222    /// given back from functions like [`crate::extrinsics::decode_extrinsic`] or
223    /// [`crate::storage::decode_storage_key`].
224    pub use scale_decode::visitor::decode_with_visitor;
225
226    /// An alias to the underlying [`scale_decode`] crate.
227    pub use scale_decode;
228}
229
230#[cfg(test)]
231mod test {
232    use crate::methods::extrinsic_type_info::ExtrinsicTypeInfo;
233    use crate::methods::runtime_api_type_info::RuntimeApiTypeInfo;
234    use crate::methods::storage_type_info::StorageTypeInfo;
235    use crate::methods::view_function_type_info::ViewFunctionTypeInfo;
236    use crate::utils::ToTypeRegistry;
237    use scale_info_legacy::type_registry::TypeRegistryResolveError;
238    use scale_info_legacy::{ChainTypeRegistry, LookupName};
239    use scale_type_resolver::Field;
240
241    // This will panic if there is any issue decoding the legacy types we provide.
242    #[test]
243    fn test_deserializing_legacy_types() {
244        let _ = crate::legacy_types::polkadot::relay_chain();
245        let _ = crate::legacy_types::kusama::relay_chain();
246        let _ = crate::legacy_types::kusama::asset_hub();
247    }
248
249    fn legacy_types() -> [(&'static str, ChainTypeRegistry); 3] {
250        [
251            ("Polkadot RC", crate::legacy_types::polkadot::relay_chain()),
252            ("Kusama RC", crate::legacy_types::kusama::relay_chain()),
253            ("Kusama AH", crate::legacy_types::kusama::asset_hub()),
254        ]
255    }
256
257    fn all_type_registry_sets(
258        registry: &scale_info_legacy::ChainTypeRegistry,
259    ) -> impl Iterator<Item = scale_info_legacy::TypeRegistrySet<'_>> {
260        let all_spec_versions = core::iter::once(u64::MAX)
261            .chain(registry.spec_version_ranges().map(|(low, _high)| low));
262        all_spec_versions.map(|version| registry.for_spec_version(version))
263    }
264
265    #[test]
266    fn test_legacy_types_have_sane_type_names() {
267        // We ignore these ones:
268        let builtins = &[
269            "u8", "u16", "u32", "u64", "u128", "u256", "i8", "i16", "i32", "i64", "i128", "i256",
270            "char", "bool", "str",
271        ];
272
273        for (chain, types) in legacy_types() {
274            for types in all_type_registry_sets(&types) {
275                for ty in types.keys() {
276                    if let Some(path_and_name) = ty.name() {
277                        let name = path_and_name.split("::").last().unwrap();
278
279                        if builtins.contains(&name) {
280                            continue;
281                        }
282
283                        if name.starts_with(|c: char| !c.is_uppercase() || !c.is_ascii_alphabetic())
284                        {
285                            panic!("{chain}: {ty} does not begin with an uppercase letter");
286                        }
287                        if name.contains(|c: char| !c.is_ascii_alphanumeric()) {
288                            panic!("{chain}: {ty} contains a non-ascii-alphanumeric character");
289                        }
290                    }
291                }
292            }
293        }
294    }
295
296    #[test]
297    fn test_legacy_types_have_sane_field_names() {
298        fn assert_sane<'a>(
299            chain: &str,
300            ty: &LookupName,
301            fields: impl Iterator<Item = Field<'a, LookupName>>,
302        ) {
303            let field_names: Vec<_> = fields.map(|f| f.name).collect();
304
305            let all_fields_named = field_names.iter().all(|n| n.is_some());
306            let all_fields_unnamed = field_names.iter().all(|n| n.is_none());
307
308            if !(all_fields_named || all_fields_unnamed) {
309                panic!("{chain}: All fields must be named or unnamed, but aren't in '{ty}'");
310            }
311            if all_fields_named {
312                for name in field_names.into_iter().map(|n| n.unwrap()) {
313                    let Some(fst) = name.chars().next() else {
314                        panic!("{chain}: {ty} has a present but empty field name");
315                    };
316                    if !fst.is_ascii_alphabetic() {
317                        panic!(
318                            "{chain}: {ty} field name '{name}' is invalid (does not start with ascii letter)"
319                        );
320                    }
321                    if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
322                        panic!(
323                            "{chain}: {ty} field name '{name}' is invalid (non-ascii-letter-or-number-or-underscore present in the name"
324                        );
325                    }
326                    if name.contains(|c: char| c.is_uppercase()) {
327                        panic!(
328                            "{chain}: {ty} field name '{name}' contains uppercase. Field names should be lowercase only"
329                        );
330                    }
331                }
332            }
333        }
334
335        for (chain, types) in legacy_types() {
336            for types in all_type_registry_sets(&types) {
337                for ty in types.keys() {
338                    let visitor = scale_type_resolver::visitor::new((), |_, _| ())
339                        .visit_variant(|_ctx, _path, vars| {
340                            for variant in vars {
341                                assert_sane(chain, &ty, variant.fields);
342                            }
343                        })
344                        .visit_composite(|_ctx, _path, fields| {
345                            assert_sane(chain, &ty, fields);
346                        });
347
348                    if let Err(e) = types.resolve_type(ty.clone(), visitor) {
349                        match e {
350                            TypeRegistryResolveError::UnexpectedBitOrderType
351                            | TypeRegistryResolveError::UnexpectedBitStoreType => {
352                                /* Ignore these */
353                            }
354                            e => panic!("{chain}: Cannot resolve type '{ty}': {e}"),
355                        }
356                    }
357                }
358            }
359        }
360    }
361
362    #[test]
363    fn test_legacy_types_have_sane_variant_names() {
364        fn assert_sane(chain: &str, ty: &LookupName, variant_name: &str) {
365            let Some(fst) = variant_name.chars().next() else {
366                panic!("{chain}: Enum {ty} has an empty variant");
367            };
368
369            if !fst.is_uppercase() {
370                panic!(
371                    "{chain}: Enum {ty} variant name '{variant_name}' should start with an uppercase letter"
372                );
373            }
374            if !fst.is_ascii_alphabetic() {
375                panic!(
376                    "{chain}: Enum {ty} variant name '{variant_name}' should start with an ASCII letter"
377                );
378            }
379            if !variant_name.chars().all(|c| c.is_ascii_alphanumeric()) {
380                panic!(
381                    "{chain}: Enum {ty} variant name '{variant_name}' is invalid (non-ascii-letter-or-number present in the name"
382                );
383            }
384        }
385
386        for (chain, types) in legacy_types() {
387            for types in all_type_registry_sets(&types) {
388                for ty in types.keys() {
389                    let visitor = scale_type_resolver::visitor::new((), |_, _| ()).visit_variant(
390                        |_ctx, _path, vars| {
391                            for variant in vars {
392                                assert_sane(chain, &ty, variant.name);
393                            }
394                        },
395                    );
396
397                    if let Err(e) = types.resolve_type(ty.clone(), visitor) {
398                        match e {
399                            TypeRegistryResolveError::UnexpectedBitOrderType
400                            | TypeRegistryResolveError::UnexpectedBitStoreType => {
401                                /* Ignore these */
402                            }
403                            e => panic!("{chain}: Cannot resolve type '{ty}': {e}"),
404                        }
405                    }
406                }
407            }
408        }
409    }
410
411    macro_rules! impls_trait {
412        ($type:ty, $trait:path) => {
413            const _: () = {
414                const fn assert_impl<T: $trait>() {}
415                assert_impl::<$type>();
416            };
417        };
418    }
419
420    // Just a sanity check that all of the metadata versions we expect implement
421    // all of the key traits. Makes it harder to miss something when adding a new metadata
422    // version; just add it below and implement the traits until everything compiles.
423    #[rustfmt::skip]
424    const _: () = {
425        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ExtrinsicTypeInfo);
426        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ExtrinsicTypeInfo);
427        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ExtrinsicTypeInfo);
428        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ExtrinsicTypeInfo);
429        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ExtrinsicTypeInfo);
430        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ExtrinsicTypeInfo);
431        impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ExtrinsicTypeInfo);
432        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ExtrinsicTypeInfo);
433        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ExtrinsicTypeInfo);
434
435        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, StorageTypeInfo);
436        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, StorageTypeInfo);
437        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, StorageTypeInfo);
438        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, StorageTypeInfo);
439        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, StorageTypeInfo);
440        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, StorageTypeInfo);
441        impls_trait!(frame_metadata::v14::RuntimeMetadataV14, StorageTypeInfo);
442        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, StorageTypeInfo);
443        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, StorageTypeInfo);
444
445        // Only V16+ metadata contains any view function information. Prior to this,
446        // hardly any view functions existed. We _could_ extend our legacy type information
447        // to support them if necessary, but it's unlikely it will be.
448        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ViewFunctionTypeInfo);
449
450        // Only V15+ metadata has Runtime API info in. For earlier, we lean on
451        // our scale-Info-legacy type registry to provide the information.
452        impls_trait!(scale_info_legacy::TypeRegistry, RuntimeApiTypeInfo);
453        impls_trait!(scale_info_legacy::TypeRegistrySet, RuntimeApiTypeInfo);
454        impls_trait!(frame_metadata::v15::RuntimeMetadataV15, RuntimeApiTypeInfo);
455        impls_trait!(frame_metadata::v16::RuntimeMetadataV16, RuntimeApiTypeInfo);
456
457        // This is a legacy trait and so only legacy metadata versions implement it:
458        impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToTypeRegistry);
459        impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToTypeRegistry);
460        impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToTypeRegistry);
461        impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToTypeRegistry);
462        impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToTypeRegistry);
463        impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToTypeRegistry);
464    };
465}