Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog].

[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/

## 0.8.0 (2025-05-07)

- Support `frame-metadata` v23. That stabilized V16 metadata, so we implement the relevant traits for that here to support it.

## 0.7.1 (2025-04-23)

Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "frame-decode"
version = "0.7.1"
version = "0.8.0"
edition = "2021"
description = "Decode extrinsics and storage from Substrate based chains"
license = "Apache-2.0"
Expand Down Expand Up @@ -34,7 +34,7 @@ legacy = [
]

[dependencies]
frame-metadata = { version = ">=20.0.0, <=21.0.0", features = ["current"], default-features = false }
frame-metadata = { version = "23.0.0", features = ["current"], default-features = false }
parity-scale-codec = { version = "3.6.12", default-features = false }
scale-decode = { version = "0.16.0", default-features = false }
scale-info = { version = "2.11.4", default-features = false }
Expand Down
61 changes: 58 additions & 3 deletions src/decoding/extrinsic_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ pub struct ExtrinsicExtensionInfo<'a, TypeId> {
pub extension_ids: Vec<ExtrinsicInfoArg<'a, TypeId>>,
}

macro_rules! impl_call_arg_ids_body_for_v14_to_v15 {
macro_rules! impl_call_arg_ids_body_for_v14_to_v16 {
($self:ident, $pallet_index:ident, $call_index:ident) => {{
let pallet = $self
.pallets
Expand Down Expand Up @@ -318,7 +318,7 @@ impl ExtrinsicTypeInfo for frame_metadata::v14::RuntimeMetadataV14 {
pallet_index: u8,
call_index: u8,
) -> Result<ExtrinsicCallInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
impl_call_arg_ids_body_for_v14_to_v15!(self, pallet_index, call_index)
impl_call_arg_ids_body_for_v14_to_v16!(self, pallet_index, call_index)
}
fn get_signature_info(
&self,
Expand Down Expand Up @@ -357,7 +357,7 @@ impl ExtrinsicTypeInfo for frame_metadata::v15::RuntimeMetadataV15 {
pallet_index: u8,
call_index: u8,
) -> Result<ExtrinsicCallInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
impl_call_arg_ids_body_for_v14_to_v15!(self, pallet_index, call_index)
impl_call_arg_ids_body_for_v14_to_v16!(self, pallet_index, call_index)
}
fn get_signature_info(
&self,
Expand Down Expand Up @@ -387,6 +387,61 @@ impl ExtrinsicTypeInfo for frame_metadata::v15::RuntimeMetadataV15 {
}
}

impl ExtrinsicTypeInfo for frame_metadata::v16::RuntimeMetadataV16 {
type TypeId = u32;
fn get_call_info(
&self,
pallet_index: u8,
call_index: u8,
) -> Result<ExtrinsicCallInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
impl_call_arg_ids_body_for_v14_to_v16!(self, pallet_index, call_index)
}
fn get_signature_info(
&self,
) -> Result<ExtrinsicSignatureInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
Ok(ExtrinsicSignatureInfo {
address_id: self.extrinsic.address_ty.id,
signature_id: self.extrinsic.signature_ty.id,
})
}
fn get_extension_info(
&self,
extension_version: Option<u8>,
) -> Result<ExtrinsicExtensionInfo<'_, Self::TypeId>, ExtrinsicInfoError<'_>> {
// If no extension version is provided (ie we are decoding a v4 transaction),
// we always use version 0 of the transaction extensions. Else, we try to find
// the version of transaction extensions that the transaction declared it's
// using.
let extension_version = extension_version.unwrap_or(0);

let extension_indexes = self
.extrinsic
.transaction_extensions_by_version
.get(&extension_version)
.ok_or(ExtrinsicInfoError::ExtrinsicExtensionVersionNotSupported {
extension_version,
})?;

let extension_ids = extension_indexes
.iter()
.map(|idx| {
let ext = self
.extrinsic
.transaction_extensions
.get(idx.0 as usize)
.expect("Index in transaction_extensions_by_version should exist in transaction_extensions");

ExtrinsicInfoArg {
id: ext.ty.id,
name: Cow::Borrowed(&ext.identifier),
}
})
.collect();

Ok(ExtrinsicExtensionInfo { extension_ids })
}
}

fn get_v14_extrinsic_parts(
metadata: &frame_metadata::v14::RuntimeMetadataV14,
) -> Result<ExtrinsicParts, ExtrinsicInfoError<'_>> {
Expand Down
12 changes: 9 additions & 3 deletions src/decoding/storage_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub enum StorageHasher {
Identity,
}

macro_rules! impl_storage_type_info_for_v14_to_v15 {
macro_rules! impl_storage_type_info_for_v14_to_v16 {
($path:path, $name:ident, $to_storage_hasher:ident) => {
const _: () = {
use $path as path;
Expand Down Expand Up @@ -298,16 +298,21 @@ macro_rules! impl_storage_type_info_for_v14_to_v15 {
};
}

impl_storage_type_info_for_v14_to_v15!(
impl_storage_type_info_for_v14_to_v16!(
frame_metadata::v14,
RuntimeMetadataV14,
to_storage_hasher_v14
);
impl_storage_type_info_for_v14_to_v15!(
impl_storage_type_info_for_v14_to_v16!(
frame_metadata::v15,
RuntimeMetadataV15,
to_storage_hasher_v15
);
impl_storage_type_info_for_v14_to_v16!(
frame_metadata::v16,
RuntimeMetadataV16,
to_storage_hasher_v16
);

macro_rules! to_latest_storage_hasher {
($ident:ident, $path:path) => {
Expand All @@ -327,6 +332,7 @@ macro_rules! to_latest_storage_hasher {

to_latest_storage_hasher!(to_storage_hasher_v14, frame_metadata::v14::StorageHasher);
to_latest_storage_hasher!(to_storage_hasher_v15, frame_metadata::v15::StorageHasher);
to_latest_storage_hasher!(to_storage_hasher_v16, frame_metadata::v16::StorageHasher);

#[cfg(feature = "legacy")]
mod legacy {
Expand Down
64 changes: 64 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,67 @@ pub mod helpers {
///
pub use scale_decode;
}

#[cfg(all(test, feature = "legacy"))]
mod test {
use crate::decoding::extrinsic_type_info::ExtrinsicTypeInfo;
use crate::decoding::storage_type_info::StorageTypeInfo;
use crate::utils::{InfoAndResolver, ToStorageEntriesList, ToTypeRegistry};

macro_rules! impls_trait {
($type:ty, $trait:path) => {
const _: () = {
const fn assert_impl<T: $trait>() {}
assert_impl::<$type>();
};
};
}

// Just a sanity check that all of the metadata versions we expect implement
// all of the key traits. Makes it harder to miss something when adding a new metadata
// version; just add it below and implement the traits until everything compiles.
#[rustfmt::skip]
const _: () = {
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, InfoAndResolver);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, InfoAndResolver);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, InfoAndResolver);

impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ExtrinsicTypeInfo);

impls_trait!(frame_metadata::v8::RuntimeMetadataV8, StorageTypeInfo);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, StorageTypeInfo);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, StorageTypeInfo);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, StorageTypeInfo);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, StorageTypeInfo);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, StorageTypeInfo);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, StorageTypeInfo);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, StorageTypeInfo);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, StorageTypeInfo);

impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToStorageEntriesList);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToStorageEntriesList);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToStorageEntriesList);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToStorageEntriesList);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToStorageEntriesList);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToStorageEntriesList);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ToStorageEntriesList);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ToStorageEntriesList);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ToStorageEntriesList);

// This is a legacy trait and so only legacy metadata versions implement it:
impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToTypeRegistry);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToTypeRegistry);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToTypeRegistry);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToTypeRegistry);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToTypeRegistry);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToTypeRegistry);
};
}
18 changes: 18 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ pub use type_registry_from_metadata::{
type_registry_from_metadata, type_registry_from_metadata_any,
};

// We don't want to expose these traits at the moment, but want to test them.
#[cfg(all(test, feature = "legacy"))]
pub use list_storage_entries::ToStorageEntriesList;
#[cfg(all(test, feature = "legacy"))]
pub use type_registry_from_metadata::ToTypeRegistry;

/// A utility function to unwrap the `DecodeDifferent` enum found in earlier metadata versions.
#[cfg(feature = "legacy")]
pub fn as_decoded<A, B>(item: &frame_metadata::decode_different::DecodeDifferent<A, B>) -> &B {
Expand Down Expand Up @@ -66,3 +72,15 @@ impl InfoAndResolver for frame_metadata::v15::RuntimeMetadataV15 {
&self.types
}
}

impl InfoAndResolver for frame_metadata::v16::RuntimeMetadataV16 {
type Info = frame_metadata::v16::RuntimeMetadataV16;
type Resolver = scale_info::PortableRegistry;

fn info(&self) -> &Self::Info {
self
}
fn resolver(&self) -> &Self::Resolver {
&self.types
}
}
10 changes: 5 additions & 5 deletions src/utils/list_storage_entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ pub fn list_storage_entries_any(
RuntimeMetadata::V13(_opaque) => Box::new(core::iter::empty()),
RuntimeMetadata::V14(m) => Box::new(m.storage_entries_list()),
RuntimeMetadata::V15(m) => Box::new(m.storage_entries_list()),
// TODO: support unstable metadata v16 https://github.com/paritytech/frame-decode/issues/11
RuntimeMetadata::V16(_opaque) => Box::new(core::iter::empty()),
RuntimeMetadata::V16(m) => Box::new(m.storage_entries_list()),
}
}

Expand Down Expand Up @@ -142,7 +141,7 @@ const _: () = {
impl_storage_entries_list_for_v8_to_v13!(frame_metadata::v13::RuntimeMetadataV13);
};

macro_rules! impl_storage_entries_list_for_v14_to_v15 {
macro_rules! impl_storage_entries_list_for_v14_to_v16 {
($path:path) => {
impl ToStorageEntriesList for $path {
fn storage_entries_list(&self) -> impl Iterator<Item = StorageEntry<'_>> {
Expand All @@ -164,8 +163,9 @@ macro_rules! impl_storage_entries_list_for_v14_to_v15 {
};
}

impl_storage_entries_list_for_v14_to_v15!(frame_metadata::v14::RuntimeMetadataV14);
impl_storage_entries_list_for_v14_to_v15!(frame_metadata::v15::RuntimeMetadataV15);
impl_storage_entries_list_for_v14_to_v16!(frame_metadata::v14::RuntimeMetadataV14);
impl_storage_entries_list_for_v14_to_v16!(frame_metadata::v15::RuntimeMetadataV15);
impl_storage_entries_list_for_v14_to_v16!(frame_metadata::v16::RuntimeMetadataV16);

enum Either<L, R> {
Left(L),
Expand Down