Skip to main content

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