subxt-metadata 0.50.1

Command line utilities for checking metadata compatibility between nodes.
Documentation
// Copyright 2019-2026 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use super::TryFromError;

use crate::utils::variant_index::VariantIndex;
use crate::{
    ConstantMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner,
    RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner, StorageEntryMetadata, StorageMetadata,
    TransactionExtensionMetadataInner, utils::ordered_map::OrderedMap,
};
use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use frame_decode::runtime_apis::RuntimeApiTypeInfo;
use frame_decode::storage::StorageTypeInfo;
use frame_metadata::v15;
use hashbrown::HashMap;
use scale_info::form::PortableForm;

impl TryFrom<v15::RuntimeMetadataV15> for Metadata {
    type Error = TryFromError;
    fn try_from(m: v15::RuntimeMetadataV15) -> Result<Self, TryFromError> {
        let mut pallets = OrderedMap::new();
        let mut pallets_by_index = HashMap::new();
        for (pos, p) in m.pallets.iter().enumerate() {
            let name = p.name.clone();

            let storage = match &p.storage {
                None => None,
                Some(s) => Some(StorageMetadata {
                    prefix: s.prefix.clone(),
                    entries: s
                        .entries
                        .iter()
                        .map(|s| {
                            let entry_name = s.name.clone();
                            let storage_info = m
                                .storage_info(&name, &entry_name)
                                .map_err(|e| e.into_owned())?
                                .into_owned();
                            let storage_entry = StorageEntryMetadata {
                                name: entry_name.clone(),
                                info: storage_info,
                                docs: s.docs.clone(),
                            };

                            Ok::<_, TryFromError>((entry_name, storage_entry))
                        })
                        .collect::<Result<_, TryFromError>>()?,
                }),
            };

            let constants = p.constants.iter().map(|c| {
                let name = c.name.clone();
                (name, from_constant_metadata(c.clone()))
            });

            let call_variant_index =
                VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), &m.types);
            let error_variant_index =
                VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), &m.types);
            let event_variant_index =
                VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), &m.types);

            pallets_by_index.insert(p.index, pos);
            pallets.push_insert(
                name.clone(),
                PalletMetadataInner {
                    name,
                    call_index: p.index,
                    event_index: p.index,
                    error_index: p.index,
                    storage,
                    call_ty: p.calls.as_ref().map(|c| c.ty.id),
                    call_variant_index,
                    event_ty: p.event.as_ref().map(|e| e.ty.id),
                    event_variant_index,
                    error_ty: p.error.as_ref().map(|e| e.ty.id),
                    error_variant_index,
                    constants: constants.collect(),
                    view_functions: Default::default(),
                    associated_types: Default::default(),
                    docs: p.docs.clone(),
                },
            );
        }

        let apis = m
            .apis
            .iter()
            .map(|api| {
                let trait_name = api.name.clone();
                let methods = api
                    .methods
                    .iter()
                    .map(|method| {
                        let method_name = method.name.clone();
                        let method_info = RuntimeApiMethodMetadataInner {
                            info: m
                                .runtime_api_info(&trait_name, &method.name)
                                .map_err(|e| e.into_owned())?
                                .into_owned(),
                            name: method.name.clone(),
                            docs: method.docs.clone(),
                        };
                        Ok((method_name, method_info))
                    })
                    .collect::<Result<_, TryFromError>>()?;

                let runtime_api_metadata = RuntimeApiMetadataInner {
                    name: trait_name.clone(),
                    methods,
                    docs: api.docs.clone(),
                };
                Ok((trait_name, runtime_api_metadata))
            })
            .collect::<Result<_, TryFromError>>()?;

        let dispatch_error_ty = m
            .types
            .types
            .iter()
            .find(|ty| ty.ty.path.segments == ["sp_runtime", "DispatchError"])
            .map(|ty| ty.id);

        Ok(Metadata {
            types: m.types,
            pallets,
            pallets_by_call_index: pallets_by_index.clone(),
            pallets_by_error_index: pallets_by_index.clone(),
            pallets_by_event_index: pallets_by_index,
            extrinsic: from_extrinsic_metadata(m.extrinsic),
            dispatch_error_ty,
            apis,
            outer_enums: OuterEnumsMetadata {
                call_enum_ty: m.outer_enums.call_enum_ty.id,
                event_enum_ty: m.outer_enums.event_enum_ty.id,
                error_enum_ty: m.outer_enums.error_enum_ty.id,
            },
            custom: m.custom,
        })
    }
}

fn from_signed_extension_metadata(
    value: v15::SignedExtensionMetadata<PortableForm>,
) -> TransactionExtensionMetadataInner {
    TransactionExtensionMetadataInner {
        identifier: value.identifier,
        extra_ty: value.ty.id,
        additional_ty: value.additional_signed.id,
    }
}

fn from_extrinsic_metadata(value: v15::ExtrinsicMetadata<PortableForm>) -> ExtrinsicMetadata {
    let transaction_extensions: Vec<_> = value
        .signed_extensions
        .into_iter()
        .map(from_signed_extension_metadata)
        .collect();

    let transaction_extension_indexes = (0..transaction_extensions.len() as u32).collect();

    ExtrinsicMetadata {
        supported_versions: vec![value.version],
        transaction_extensions,
        address_ty: value.address_ty.id,
        signature_ty: value.signature_ty.id,
        transaction_extensions_by_version: BTreeMap::from_iter([(
            0,
            transaction_extension_indexes,
        )]),
    }
}

fn from_constant_metadata(s: v15::PalletConstantMetadata<PortableForm>) -> ConstantMetadata {
    ConstantMetadata {
        name: s.name,
        ty: s.ty.id,
        value: s.value,
        docs: s.docs,
    }
}