mod readonly_array;
mod u8_map;
mod version_14;
use crate::{ScaleInfoTypeId, Type, TypeId};
use codec::Decode;
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
use readonly_array::ReadonlyArray;
use scale_info::{form::PortableForm, PortableRegistry};
use u8_map::U8Map;
type TypeDefVariant = scale_info::TypeDefVariant<PortableForm>;
type SignedExtensionMetadata = frame_metadata::SignedExtensionMetadata<PortableForm>;
type StorageEntryMetadata = frame_metadata::v14::StorageEntryMetadata<scale_info::form::PortableForm>;
#[derive(Debug, Clone, thiserror::Error)]
pub enum MetadataError {
#[error("metadata version {0} is not supported")]
UnsupportedVersion(u32),
#[error("{0}")]
CodecError(#[from] codec::Error),
#[error("unexpected type; expecting a Variant type, but got {got}")]
ExpectedVariantType { got: String },
#[error("could not find type with ID {0}")]
TypeNotFound(u32),
}
#[derive(Debug)]
pub struct Metadata {
extrinsic: MetadataExtrinsic,
pallet_calls_by_index: U8Map<MetadataPalletCalls>,
pallet_storage: ReadonlyArray<MetadataPalletStorage>,
types: PortableRegistry,
}
impl Metadata {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, MetadataError> {
log::trace!("Decoding metadata");
let meta = RuntimeMetadataPrefixed::decode(&mut &*bytes)?;
Self::from_runtime_metadata(meta.1)
}
pub fn from_runtime_metadata(metadata: RuntimeMetadata) -> Result<Self, MetadataError> {
match metadata {
RuntimeMetadata::V14(meta_v14) => {
log::trace!("V14 metadata found.");
version_14::decode(meta_v14)
}
unsupported_meta => Err(MetadataError::UnsupportedVersion(unsupported_meta.version())),
}
}
pub fn extrinsic(&self) -> &MetadataExtrinsic {
&self.extrinsic
}
pub fn resolve<Id: Into<TypeId>>(&self, id: Id) -> Option<&Type> {
self.types.resolve(id.into().id())
}
pub(crate) fn types(&self) -> &PortableRegistry {
&self.types
}
pub(crate) fn storage_entry(&self, loc: StorageLocation) -> StorageEntry<'_> {
let pallet =
self.pallet_storage.get(loc.prefix_index).expect("Storage entry with the prefix index given should exist");
let entry =
pallet.storage_entries.get(loc.entry_index).expect("Storage entry with the entry index given should exist");
StorageEntry { prefix: &pallet.prefix, metadata: entry }
}
pub(crate) fn storage_entries(&self) -> impl Iterator<Item = &MetadataPalletStorage> {
self.pallet_storage.iter()
}
pub(crate) fn call_variant_by_enum_index(
&self,
pallet: u8,
call: u8,
) -> Option<(&str, &scale_info::Variant<PortableForm>)> {
self.pallet_calls_by_index.get(pallet).and_then(|p| {
p.calls.as_ref().and_then(|calls| {
let type_def_variant = self.get_variant(calls.calls_type_id)?;
let index = *calls.call_variant_indexes.get(call)?;
let variant = type_def_variant.variants().get(index)?;
Some((&*p.name, variant))
})
})
}
fn get_variant(&self, ty: ScaleInfoTypeId) -> Option<&TypeDefVariant> {
self.types.resolve(ty.id()).and_then(|ty| match ty.type_def() {
scale_info::TypeDef::Variant(variant) => Some(variant),
_ => None,
})
}
}
#[derive(Debug)]
pub(crate) struct MetadataPalletStorage {
prefix: String,
storage_entries: ReadonlyArray<StorageEntryMetadata>,
}
impl MetadataPalletStorage {
pub fn prefix(&self) -> &str {
&self.prefix
}
pub fn entries(&self) -> impl Iterator<Item = &StorageEntryMetadata> {
self.storage_entries.iter()
}
}
#[derive(Debug)]
struct MetadataPalletCalls {
name: String,
calls: Option<MetadataCalls>,
}
#[derive(Debug)]
struct MetadataCalls {
calls_type_id: ScaleInfoTypeId,
call_variant_indexes: U8Map<usize>,
}
#[derive(Debug, Clone)]
pub struct MetadataExtrinsic {
version: u8,
signed_extensions: Vec<SignedExtensionMetadata>,
}
impl MetadataExtrinsic {
#[allow(unused)]
pub fn version(&self) -> u8 {
self.version
}
pub(crate) fn signed_extensions(&self) -> &[SignedExtensionMetadata] {
&self.signed_extensions
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct StorageLocation {
pub prefix_index: usize,
pub entry_index: usize,
}
pub(crate) struct StorageEntry<'a> {
pub prefix: &'a str,
pub metadata: &'a StorageEntryMetadata,
}