mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 10:41:01 +00:00
[v0.50.0] Allow visiting extrinsic fields in subxt_historic (#2124)
* Allow visiting extrinsic fields * fmt * Don't use local scale-decode dep * Clippy and tidy * Extend 'subxt codegen' CLI to work with legacy metadatas * Simplify historic extrinsics example now that AccountId32s have paths/names * clippy * clippy * clippy.. * Allow visiting storage values, too, and clean up extrinsic visiting a little by narrowing lifetime * Try to fix flaky test * Add custom value decode to extrinsics example * Remove useless else branch ra thought I needed * Simplify examples
This commit is contained in:
Generated
+10
-8
@@ -1953,9 +1953,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "frame-decode"
|
name = "frame-decode"
|
||||||
version = "0.13.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73d29c7f2987ea24ab2eaea315aadb9ba598188823181cdf0476049b625a5844"
|
checksum = "0fb3bfa2988ef40247e0e0eecfb171a01ad6f4e399485503aad4c413a5f236f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"frame-metadata 23.0.0",
|
"frame-metadata 23.0.0",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
@@ -4383,9 +4383,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scale-decode"
|
name = "scale-decode"
|
||||||
version = "0.16.0"
|
version = "0.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d78196772d25b90a98046794ce0fe2588b39ebdfbdc1e45b4c6c85dd43bebad"
|
checksum = "8d6ed61699ad4d54101ab5a817169259b5b0efc08152f8632e61482d8a27ca3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"primitive-types",
|
"primitive-types",
|
||||||
@@ -4398,9 +4398,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scale-decode-derive"
|
name = "scale-decode-derive"
|
||||||
version = "0.16.0"
|
version = "0.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f4b54a1211260718b92832b661025d1f1a4b6930fbadd6908e00edd265fa5f7"
|
checksum = "65cb245f7fdb489e7ba43a616cbd34427fe3ba6fe0edc1d0d250085e6c84f3ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -4464,9 +4464,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scale-info-legacy"
|
name = "scale-info-legacy"
|
||||||
version = "0.3.2"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06423f0d7ea951547143aff4695c4c3e821e66c9b80729a3ff55fa93d23e93e6"
|
checksum = "2500adfb429a0ffda37919df92c05d0c1359c10e0444c17253c84b84dfce542f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.15.3",
|
"hashbrown 0.15.3",
|
||||||
"scale-type-resolver",
|
"scale-type-resolver",
|
||||||
@@ -5658,6 +5658,7 @@ version = "0.44.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"frame-decode",
|
||||||
"frame-metadata 23.0.0",
|
"frame-metadata 23.0.0",
|
||||||
"heck",
|
"heck",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -5667,6 +5668,7 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quote",
|
"quote",
|
||||||
"scale-info",
|
"scale-info",
|
||||||
|
"scale-info-legacy",
|
||||||
"scale-typegen 0.12.0",
|
"scale-typegen 0.12.0",
|
||||||
"scale-typegen-description",
|
"scale-typegen-description",
|
||||||
"scale-value",
|
"scale-value",
|
||||||
|
|||||||
+3
-3
@@ -81,7 +81,7 @@ darling = "0.20.10"
|
|||||||
derive-where = "1.2.7"
|
derive-where = "1.2.7"
|
||||||
either = { version = "1.13.0", default-features = false }
|
either = { version = "1.13.0", default-features = false }
|
||||||
finito = { version = "0.1.0", default-features = false }
|
finito = { version = "0.1.0", default-features = false }
|
||||||
frame-decode = { version = "0.14.0", default-features = false }
|
frame-decode = { version = "0.15.0", default-features = false }
|
||||||
frame-metadata = { version = "23.0.0", default-features = false }
|
frame-metadata = { version = "23.0.0", default-features = false }
|
||||||
futures = { version = "0.3.31", default-features = false, features = ["std"] }
|
futures = { version = "0.3.31", default-features = false, features = ["std"] }
|
||||||
getrandom = { version = "0.2", default-features = false }
|
getrandom = { version = "0.2", default-features = false }
|
||||||
@@ -100,10 +100,10 @@ regex = { version = "1.11.0", default-features = false }
|
|||||||
scale-info = { version = "2.11.4", default-features = false }
|
scale-info = { version = "2.11.4", default-features = false }
|
||||||
scale-value = { version = "0.18.1", default-features = false }
|
scale-value = { version = "0.18.1", default-features = false }
|
||||||
scale-bits = { version = "0.7.0", default-features = false }
|
scale-bits = { version = "0.7.0", default-features = false }
|
||||||
scale-decode = { version = "0.16.0", default-features = false }
|
scale-decode = { version = "0.16.2", default-features = false }
|
||||||
scale-encode = { version = "0.10.0", default-features = false }
|
scale-encode = { version = "0.10.0", default-features = false }
|
||||||
scale-type-resolver = { version = "0.2.0" }
|
scale-type-resolver = { version = "0.2.0" }
|
||||||
scale-info-legacy = { version = "0.3.2", default-features = false }
|
scale-info-legacy = { version = "0.4.0", default-features = false }
|
||||||
scale-typegen = "0.12.0"
|
scale-typegen = "0.12.0"
|
||||||
scale-typegen-description = "0.11.0"
|
scale-typegen-description = "0.11.0"
|
||||||
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
|
||||||
|
|||||||
+3
-1
@@ -30,16 +30,18 @@ subxt-codegen = { workspace = true }
|
|||||||
scale-typegen = { workspace = true }
|
scale-typegen = { workspace = true }
|
||||||
subxt-utils-fetchmetadata = { workspace = true, features = ["url"] }
|
subxt-utils-fetchmetadata = { workspace = true, features = ["url"] }
|
||||||
subxt-utils-stripmetadata = { workspace = true }
|
subxt-utils-stripmetadata = { workspace = true }
|
||||||
subxt-metadata = { workspace = true }
|
subxt-metadata = { workspace = true, features = ["legacy"] }
|
||||||
subxt = { workspace = true, features = ["default"] }
|
subxt = { workspace = true, features = ["default"] }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
color-eyre = { workspace = true }
|
color-eyre = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
|
frame-decode = { workspace = true, features = ["legacy-types"] }
|
||||||
frame-metadata = { workspace = true }
|
frame-metadata = { workspace = true }
|
||||||
codec = { package = "parity-scale-codec", workspace = true }
|
codec = { package = "parity-scale-codec", workspace = true }
|
||||||
scale-info = { workspace = true }
|
scale-info = { workspace = true }
|
||||||
|
scale-info-legacy = { workspace = true }
|
||||||
scale-value = { workspace = true }
|
scale-value = { workspace = true }
|
||||||
syn = { workspace = true }
|
syn = { workspace = true }
|
||||||
quote = { workspace = true }
|
quote = { workspace = true }
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
use crate::utils::{FileOrUrl, validate_url_security};
|
use crate::utils::{FileOrUrl, validate_url_security};
|
||||||
use clap::Parser as ClapParser;
|
use clap::Parser as ClapParser;
|
||||||
use codec::Decode;
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use scale_typegen_description::scale_typegen::typegen::{
|
use scale_typegen_description::scale_typegen::typegen::{
|
||||||
settings::substitutes::path_segments,
|
settings::substitutes::path_segments,
|
||||||
validation::{registry_contains_type_path, similar_type_paths_in_registry},
|
validation::{registry_contains_type_path, similar_type_paths_in_registry},
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
use subxt_codegen::CodegenBuilder;
|
use subxt_codegen::CodegenBuilder;
|
||||||
use subxt_metadata::Metadata;
|
use subxt_metadata::Metadata;
|
||||||
|
|
||||||
@@ -28,6 +28,12 @@ pub struct Opts {
|
|||||||
/// Additional attributes
|
/// Additional attributes
|
||||||
#[clap(long = "attribute")]
|
#[clap(long = "attribute")]
|
||||||
attributes: Vec<String>,
|
attributes: Vec<String>,
|
||||||
|
/// Path to legacy type definitions (required for metadatas pre-V14)
|
||||||
|
#[clap(long)]
|
||||||
|
legacy_types: Option<PathBuf>,
|
||||||
|
/// The spec version of the legacy metadata (required for metadatas pre-V14)
|
||||||
|
#[clap(long)]
|
||||||
|
legacy_spec_version: Option<u64>,
|
||||||
/// Additional derives for a given type.
|
/// Additional derives for a given type.
|
||||||
///
|
///
|
||||||
/// Example 1: `--derive-for-type my_module::my_type=serde::Serialize`.
|
/// Example 1: `--derive-for-type my_module::my_type=serde::Serialize`.
|
||||||
@@ -145,9 +151,20 @@ pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Re
|
|||||||
validate_url_security(opts.file_or_url.url.as_ref(), opts.allow_insecure)?;
|
validate_url_security(opts.file_or_url.url.as_ref(), opts.allow_insecure)?;
|
||||||
|
|
||||||
let bytes = opts.file_or_url.fetch().await?;
|
let bytes = opts.file_or_url.fetch().await?;
|
||||||
|
let legacy_types = opts
|
||||||
|
.legacy_types
|
||||||
|
.map(|path| {
|
||||||
|
let bytes = std::fs::read(path).map_err(|e| eyre!("Cannot read legacy_types: {e}"))?;
|
||||||
|
let types = frame_decode::legacy_types::from_bytes(&bytes)
|
||||||
|
.map_err(|e| eyre!("Cannot deserialize legacy_types: {e}"))?;
|
||||||
|
Ok::<_, color_eyre::eyre::Error>(types)
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
codegen(
|
codegen(
|
||||||
&bytes,
|
&bytes,
|
||||||
|
legacy_types,
|
||||||
|
opts.legacy_spec_version,
|
||||||
opts.derives,
|
opts.derives,
|
||||||
opts.attributes,
|
opts.attributes,
|
||||||
opts.derives_for_type,
|
opts.derives_for_type,
|
||||||
@@ -175,6 +192,8 @@ impl syn::parse::Parse for OuterAttribute {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn codegen(
|
fn codegen(
|
||||||
metadata_bytes: &[u8],
|
metadata_bytes: &[u8],
|
||||||
|
legacy_types: Option<scale_info_legacy::ChainTypeRegistry>,
|
||||||
|
legacy_spec_version: Option<u64>,
|
||||||
raw_derives: Vec<String>,
|
raw_derives: Vec<String>,
|
||||||
raw_attributes: Vec<String>,
|
raw_attributes: Vec<String>,
|
||||||
derives_for_type: Vec<DeriveForType>,
|
derives_for_type: Vec<DeriveForType>,
|
||||||
@@ -211,8 +230,79 @@ fn codegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let metadata = {
|
let metadata = {
|
||||||
let mut metadata = subxt_metadata::Metadata::decode(&mut &*metadata_bytes)
|
let runtime_metadata = subxt_metadata::decode_runtime_metadata(metadata_bytes)?;
|
||||||
.map_err(|e| eyre!("Cannot decode the provided metadata: {e}"))?;
|
let mut metadata = match runtime_metadata {
|
||||||
|
// Too old to work with:
|
||||||
|
frame_metadata::RuntimeMetadata::V0(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V1(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V2(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V3(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V4(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V5(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V6(_)
|
||||||
|
| frame_metadata::RuntimeMetadata::V7(_) => {
|
||||||
|
Err(eyre!("Metadata V1-V7 cannot be decoded from"))
|
||||||
|
}
|
||||||
|
// Converting legacy metadatas:
|
||||||
|
frame_metadata::RuntimeMetadata::V8(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V8 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V8 metadata"))?;
|
||||||
|
Metadata::from_v8(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V8 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V9(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V9 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V9 metadata"))?;
|
||||||
|
Metadata::from_v9(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V9 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V10(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V10 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V10 metadata"))?;
|
||||||
|
Metadata::from_v10(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V10 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V11(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V11 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V11 metadata"))?;
|
||||||
|
Metadata::from_v11(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V11 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V12(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V12 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V12 metadata"))?;
|
||||||
|
Metadata::from_v12(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V12 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V13(md) => {
|
||||||
|
let legacy_types = legacy_types
|
||||||
|
.ok_or_else(|| eyre!("--legacy-types needed to load V13 metadata"))?;
|
||||||
|
let legacy_spec = legacy_spec_version
|
||||||
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V13 metadata"))?;
|
||||||
|
Metadata::from_v13(&md, &legacy_types.for_spec_version(legacy_spec))
|
||||||
|
.map_err(|e| eyre!("Cannot load V13 metadata: {e}"))
|
||||||
|
}
|
||||||
|
// Converting modern metadatas:
|
||||||
|
frame_metadata::RuntimeMetadata::V14(md) => {
|
||||||
|
Metadata::from_v14(md).map_err(|e| eyre!("Cannot load V14 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V15(md) => {
|
||||||
|
Metadata::from_v15(md).map_err(|e| eyre!("Cannot load V15 metadata: {e}"))
|
||||||
|
}
|
||||||
|
frame_metadata::RuntimeMetadata::V16(md) => {
|
||||||
|
Metadata::from_v16(md).map_err(|e| eyre!("Cannot load V16 metadata: {e}"))
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
// Run this first to ensure type paths are unique (which may result in 1,2,3 suffixes being added
|
// Run this first to ensure type paths are unique (which may result in 1,2,3 suffixes being added
|
||||||
// to type paths), so that when we validate derives/substitutions below, they are allowed for such
|
// to type paths), so that when we validate derives/substitutions below, they are allowed for such
|
||||||
|
|||||||
+2
-2
@@ -45,7 +45,7 @@ jsonrpsee = [
|
|||||||
subxt-rpcs = { workspace = true }
|
subxt-rpcs = { workspace = true }
|
||||||
frame-decode = { workspace = true, features = ["legacy", "legacy-types"] }
|
frame-decode = { workspace = true, features = ["legacy", "legacy-types"] }
|
||||||
frame-metadata = { workspace = true, features = ["std", "legacy"] }
|
frame-metadata = { workspace = true, features = ["std", "legacy"] }
|
||||||
scale-type-resolver = { workspace = true }
|
scale-type-resolver = { workspace = true, features = ["scale-info"] }
|
||||||
codec = { workspace = true }
|
codec = { workspace = true }
|
||||||
primitive-types = { workspace = true }
|
primitive-types = { workspace = true }
|
||||||
scale-info = { workspace = true }
|
scale-info = { workspace = true }
|
||||||
@@ -60,4 +60,4 @@ futures = { workspace = true }
|
|||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
scale-value = { workspace = true }
|
scale-value = { workspace = true }
|
||||||
scale-decode = { workspace = true, features = ["derive"] }
|
scale-decode = { workspace = true, features = ["derive"] }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use subxt_historic::{Error, OnlineClient, PolkadotConfig};
|
use subxt_historic::{OnlineClient, PolkadotConfig};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Error> {
|
async fn main() -> Result<(), Box<dyn core::error::Error + Send + Sync + 'static>> {
|
||||||
// Configuration for the Polkadot relay chain.
|
// Configuration for the Polkadot relay chain.
|
||||||
let config = PolkadotConfig::new();
|
let config = PolkadotConfig::new();
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ async fn main() -> Result<(), Error> {
|
|||||||
let client = OnlineClient::from_url(config, "wss://rpc.polkadot.io").await?;
|
let client = OnlineClient::from_url(config, "wss://rpc.polkadot.io").await?;
|
||||||
|
|
||||||
// Iterate through some randomly selected old blocks to show how to fetch and decode extrinsics.
|
// Iterate through some randomly selected old blocks to show how to fetch and decode extrinsics.
|
||||||
for block_number in 123456.. {
|
for block_number in 1234567.. {
|
||||||
println!("=== Block {block_number} ===");
|
println!("=== Block {block_number} ===");
|
||||||
|
|
||||||
// Point the client at a specific block number. By default this will download and cache
|
// Point the client at a specific block number. By default this will download and cache
|
||||||
@@ -40,10 +40,26 @@ async fn main() -> Result<(), Error> {
|
|||||||
// scale_value::Value type, which can represent any SCALE encoded data, but if you
|
// scale_value::Value type, which can represent any SCALE encoded data, but if you
|
||||||
// have an idea of the type then you can try to decode into that type instead):
|
// have an idea of the type then you can try to decode into that type instead):
|
||||||
for field in extrinsic.call().fields().iter() {
|
for field in extrinsic.call().fields().iter() {
|
||||||
|
// We can visit fields, which gives us the ability to inspect and decode information
|
||||||
|
// from them selectively, returning whatever we like from it. Here we demo our
|
||||||
|
// type name visitor which is defined below:
|
||||||
|
let tn = field
|
||||||
|
.visit(type_name::GetTypeName::new())?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// When visiting fields we can also decode into a custom shape like so:
|
||||||
|
let _custom_value = field.visit(value::GetValue::new())?;
|
||||||
|
|
||||||
|
// We can also obtain and decode things without the complexity of the above:
|
||||||
println!(
|
println!(
|
||||||
" {}: {}",
|
" {}: {} {}",
|
||||||
field.name(),
|
field.name(),
|
||||||
field.decode_as::<scale_value::Value>().unwrap()
|
field.decode_as::<scale_value::Value>().unwrap(),
|
||||||
|
if tn.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("(type name: {tn})")
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,3 +97,354 @@ async fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This module defines an example visitor which retrieves the name of a type.
|
||||||
|
/// This is a more advanced use case and can typically be avoided.
|
||||||
|
mod type_name {
|
||||||
|
use scale_decode::{
|
||||||
|
Visitor,
|
||||||
|
visitor::types::{Composite, Sequence, Variant},
|
||||||
|
visitor::{TypeIdFor, Unexpected},
|
||||||
|
};
|
||||||
|
use scale_type_resolver::TypeResolver;
|
||||||
|
|
||||||
|
/// This is a visitor which obtains type names.
|
||||||
|
pub struct GetTypeName<R> {
|
||||||
|
marker: core::marker::PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> GetTypeName<R> {
|
||||||
|
/// Construct our TypeName visitor.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GetTypeName {
|
||||||
|
marker: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TypeResolver> Visitor for GetTypeName<R> {
|
||||||
|
type Value<'scale, 'resolver> = Option<&'resolver str>;
|
||||||
|
type Error = scale_decode::Error;
|
||||||
|
type TypeResolver = R;
|
||||||
|
|
||||||
|
// Look at the path of types that have paths and return the ident from that.
|
||||||
|
fn visit_composite<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Composite<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
fn visit_variant<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Variant<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
fn visit_sequence<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Sequence<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, we return nothing as we can't find a name for the type.
|
||||||
|
fn visit_unexpected<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
_unexpected: Unexpected,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This visitor demonstrates how to decode and return a custom Value shape
|
||||||
|
mod value {
|
||||||
|
use scale_decode::{
|
||||||
|
Visitor,
|
||||||
|
visitor::TypeIdFor,
|
||||||
|
visitor::types::{Array, BitSequence, Composite, Sequence, Str, Tuple, Variant},
|
||||||
|
};
|
||||||
|
use scale_type_resolver::TypeResolver;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// A value type we're decoding into.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum Value {
|
||||||
|
Number(f64),
|
||||||
|
BigNumber(String),
|
||||||
|
Bool(bool),
|
||||||
|
Char(char),
|
||||||
|
Array(Vec<Value>),
|
||||||
|
String(String),
|
||||||
|
Address(Vec<u8>),
|
||||||
|
I256([u8; 32]),
|
||||||
|
U256([u8; 32]),
|
||||||
|
Struct(HashMap<String, Value>),
|
||||||
|
Variant(String, VariantFields),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum VariantFields {
|
||||||
|
Unnamed(Vec<Value>),
|
||||||
|
Named(HashMap<String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error we can encounter trying to decode things into a [`Value`]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ValueError {
|
||||||
|
#[error("Decode error: {0}")]
|
||||||
|
Decode(#[from] scale_decode::visitor::DecodeError),
|
||||||
|
#[error("Cannot decode bit sequence: {0}")]
|
||||||
|
CannotDecodeBitSequence(codec::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a visitor which obtains type names.
|
||||||
|
pub struct GetValue<R> {
|
||||||
|
marker: core::marker::PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> GetValue<R> {
|
||||||
|
/// Construct our TypeName visitor.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GetValue {
|
||||||
|
marker: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TypeResolver> Visitor for GetValue<R> {
|
||||||
|
type Value<'scale, 'resolver> = Value;
|
||||||
|
type Error = ValueError;
|
||||||
|
type TypeResolver = R;
|
||||||
|
|
||||||
|
fn visit_i256<'resolver>(
|
||||||
|
self,
|
||||||
|
value: &[u8; 32],
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'_, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::I256(*value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u256<'resolver>(
|
||||||
|
self,
|
||||||
|
value: &[u8; 32],
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'_, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::U256(*value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i128<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: i128,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
let attempt = value as f64;
|
||||||
|
if attempt as i128 == value {
|
||||||
|
Ok(Value::Number(attempt))
|
||||||
|
} else {
|
||||||
|
Ok(Value::BigNumber(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: i64,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_i128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i32<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: i32,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_i128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i16<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: i16,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_i128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i8<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: i8,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_i128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u128<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: u128,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
let attempt = value as f64;
|
||||||
|
if attempt as u128 == value {
|
||||||
|
Ok(Value::Number(attempt))
|
||||||
|
} else {
|
||||||
|
Ok(Value::BigNumber(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: u64,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_u128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u32<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: u32,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_u128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u16<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: u16,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_u128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u8<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: u8,
|
||||||
|
type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
self.visit_u128(value.into(), type_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bool<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: bool,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::Bool(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_char<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: char,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::Char(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_array<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
values: &mut Array<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::Array(to_array(values.remaining(), values)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_sequence<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
values: &mut Sequence<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::Array(to_array(values.remaining(), values)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Str<'scale>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::String(value.as_str()?.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_tuple<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
values: &mut Tuple<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(Value::Array(to_array(values.remaining(), values)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bitsequence<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut BitSequence<'scale>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
let bits = value.decode()?;
|
||||||
|
let mut out = Vec::with_capacity(bits.len());
|
||||||
|
for b in bits {
|
||||||
|
let b = b.map_err(ValueError::CannotDecodeBitSequence)?;
|
||||||
|
out.push(Value::Bool(b));
|
||||||
|
}
|
||||||
|
Ok(Value::Array(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_composite<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Composite<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
// Special case for ss58 addresses:
|
||||||
|
if let Some(n) = value.name()
|
||||||
|
&& n == "AccountId32"
|
||||||
|
&& value.bytes_from_start().len() == 32
|
||||||
|
{
|
||||||
|
return Ok(Value::Address(value.bytes_from_start().to_vec()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse logic for decoding variant fields:
|
||||||
|
match to_variant_fieldish(value)? {
|
||||||
|
VariantFields::Named(s) => Ok(Value::Struct(s)),
|
||||||
|
VariantFields::Unnamed(a) => Ok(Value::Array(a)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_variant<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Variant<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
let name = value.name().to_owned();
|
||||||
|
let fields = to_variant_fieldish(value.fields())?;
|
||||||
|
Ok(Value::Variant(name, fields))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_variant_fieldish<'scale, 'resolver, R: TypeResolver>(
|
||||||
|
value: &mut Composite<'scale, 'resolver, R>,
|
||||||
|
) -> Result<VariantFields, ValueError> {
|
||||||
|
// If fields are unnamed, treat as array:
|
||||||
|
if value.fields().iter().all(|f| f.name.is_none()) {
|
||||||
|
return Ok(VariantFields::Unnamed(to_array(value.remaining(), value)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise object:
|
||||||
|
let mut out = HashMap::new();
|
||||||
|
for field in value {
|
||||||
|
let field = field?;
|
||||||
|
let name = field.name().unwrap().to_string();
|
||||||
|
let value = field.decode_with_visitor(GetValue::new())?;
|
||||||
|
out.insert(name, value);
|
||||||
|
}
|
||||||
|
Ok(VariantFields::Named(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_array<'scale, 'resolver, R: TypeResolver>(
|
||||||
|
len: usize,
|
||||||
|
mut values: impl scale_decode::visitor::DecodeItemIterator<'scale, 'resolver, R>,
|
||||||
|
) -> Result<Vec<Value>, ValueError> {
|
||||||
|
let mut out = Vec::with_capacity(len);
|
||||||
|
while let Some(value) = values.decode_item(GetValue::new()) {
|
||||||
|
out.push(value?);
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use subxt_historic::{Error, OnlineClient, PolkadotConfig, ext::StreamExt};
|
use subxt_historic::{OnlineClient, PolkadotConfig, ext::StreamExt};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Error> {
|
async fn main() -> Result<(), Box<dyn core::error::Error + Send + Sync + 'static>> {
|
||||||
// Configuration for the Polkadot relay chain.
|
// Configuration for the Polkadot relay chain.
|
||||||
let config = PolkadotConfig::new();
|
let config = PolkadotConfig::new();
|
||||||
|
|
||||||
@@ -36,6 +36,12 @@ async fn main() -> Result<(), Error> {
|
|||||||
// represent any SCALE-encoded value, like so:
|
// represent any SCALE-encoded value, like so:
|
||||||
let _balance_info = entry.decode_as::<scale_value::Value>()?;
|
let _balance_info = entry.decode_as::<scale_value::Value>()?;
|
||||||
|
|
||||||
|
// We can visit the value, which is a more advanced use case and allows us to extract more
|
||||||
|
// data from the type, here the name of it, if it exists:
|
||||||
|
let tn = entry
|
||||||
|
.visit(type_name::GetTypeName::new())?
|
||||||
|
.unwrap_or("<unknown>");
|
||||||
|
|
||||||
// Or, if we know what shape to expect, we can decode the parts of the value that we care
|
// Or, if we know what shape to expect, we can decode the parts of the value that we care
|
||||||
// about directly into a static type, which is more efficient and allows easy type-safe
|
// about directly into a static type, which is more efficient and allows easy type-safe
|
||||||
// access, like so:
|
// access, like so:
|
||||||
@@ -53,7 +59,7 @@ async fn main() -> Result<(), Error> {
|
|||||||
let balance_info = entry.decode_as::<BalanceInfo>()?;
|
let balance_info = entry.decode_as::<BalanceInfo>()?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
" Single balance info from {account_id_hex} => free: {} reserved: {} misc_frozen: {} fee_frozen: {}",
|
" Single balance info from {account_id_hex} => free: {} reserved: {} misc_frozen: {} fee_frozen: {} (type name: {tn})",
|
||||||
balance_info.data.free,
|
balance_info.data.free,
|
||||||
balance_info.data.reserved,
|
balance_info.data.reserved,
|
||||||
balance_info.data.misc_frozen,
|
balance_info.data.misc_frozen,
|
||||||
@@ -105,3 +111,65 @@ async fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This module defines an example visitor which retrieves the name of a type.
|
||||||
|
/// This is a more advanced use case and can typically be avoided.
|
||||||
|
mod type_name {
|
||||||
|
use scale_decode::{
|
||||||
|
Visitor,
|
||||||
|
visitor::types::{Composite, Sequence, Variant},
|
||||||
|
visitor::{TypeIdFor, Unexpected},
|
||||||
|
};
|
||||||
|
use scale_type_resolver::TypeResolver;
|
||||||
|
|
||||||
|
/// This is a visitor which obtains type names.
|
||||||
|
pub struct GetTypeName<R> {
|
||||||
|
marker: core::marker::PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> GetTypeName<R> {
|
||||||
|
/// Construct our TypeName visitor.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GetTypeName {
|
||||||
|
marker: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TypeResolver> Visitor for GetTypeName<R> {
|
||||||
|
type Value<'scale, 'resolver> = Option<&'resolver str>;
|
||||||
|
type Error = scale_decode::Error;
|
||||||
|
type TypeResolver = R;
|
||||||
|
|
||||||
|
// Look at the path of types that have paths and return the ident from that.
|
||||||
|
fn visit_composite<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Composite<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
fn visit_variant<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Variant<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
fn visit_sequence<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
value: &mut Sequence<'scale, 'resolver, Self::TypeResolver>,
|
||||||
|
_type_id: TypeIdFor<Self>,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(value.path().last())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, we return nothing as we can't find a name for the type.
|
||||||
|
fn visit_unexpected<'scale, 'resolver>(
|
||||||
|
self,
|
||||||
|
_unexpected: Unexpected,
|
||||||
|
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::extrinsic_info::{AnyExtrinsicInfo, with_info};
|
use super::extrinsic_info::{AnyExtrinsicInfo, with_info};
|
||||||
use crate::error::ExtrinsicCallError;
|
use crate::error::ExtrinsicCallError;
|
||||||
use crate::utils::Either;
|
use crate::utils::Either;
|
||||||
|
use crate::utils::{AnyResolver, AnyTypeId};
|
||||||
use scale_info_legacy::{LookupName, TypeRegistrySet};
|
use scale_info_legacy::{LookupName, TypeRegistrySet};
|
||||||
|
|
||||||
/// This represents the call data in the extrinsic.
|
/// This represents the call data in the extrinsic.
|
||||||
@@ -53,6 +54,7 @@ impl<'extrinsics, 'atblock> ExtrinsicCall<'extrinsics, 'atblock> {
|
|||||||
pub struct ExtrinsicCallFields<'extrinsics, 'atblock> {
|
pub struct ExtrinsicCallFields<'extrinsics, 'atblock> {
|
||||||
all_bytes: &'extrinsics [u8],
|
all_bytes: &'extrinsics [u8],
|
||||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||||
|
resolver: AnyResolver<'atblock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
||||||
@@ -60,7 +62,16 @@ impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
|||||||
all_bytes: &'extrinsics [u8],
|
all_bytes: &'extrinsics [u8],
|
||||||
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
info: &'extrinsics AnyExtrinsicInfo<'atblock>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { all_bytes, info }
|
let resolver = match info {
|
||||||
|
AnyExtrinsicInfo::Legacy(info) => AnyResolver::B(info.resolver),
|
||||||
|
AnyExtrinsicInfo::Current(info) => AnyResolver::A(info.resolver),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
all_bytes,
|
||||||
|
info,
|
||||||
|
resolver,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the bytes representing the fields stored in this extrinsic.
|
/// Return the bytes representing the fields stored in this extrinsic.
|
||||||
@@ -74,11 +85,12 @@ impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over each of the fields of the extrinsic call data.
|
/// Iterate over each of the fields of the extrinsic call data.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicCallField<'extrinsics, 'atblock>> {
|
pub fn iter(&self) -> impl Iterator<Item = ExtrinsicCallField<'_, 'extrinsics, 'atblock>> {
|
||||||
match &self.info {
|
match &self.info {
|
||||||
AnyExtrinsicInfo::Legacy(info) => {
|
AnyExtrinsicInfo::Legacy(info) => {
|
||||||
Either::A(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
Either::A(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
||||||
field_bytes: &self.all_bytes[named_arg.range()],
|
field_bytes: &self.all_bytes[named_arg.range()],
|
||||||
|
resolver: &self.resolver,
|
||||||
info: AnyExtrinsicCallFieldInfo::Legacy(ExtrinsicCallFieldInfo {
|
info: AnyExtrinsicCallFieldInfo::Legacy(ExtrinsicCallFieldInfo {
|
||||||
info: named_arg,
|
info: named_arg,
|
||||||
resolver: info.resolver,
|
resolver: info.resolver,
|
||||||
@@ -88,6 +100,7 @@ impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
|||||||
AnyExtrinsicInfo::Current(info) => {
|
AnyExtrinsicInfo::Current(info) => {
|
||||||
Either::B(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
Either::B(info.info.call_data().map(|named_arg| ExtrinsicCallField {
|
||||||
field_bytes: &self.all_bytes[named_arg.range()],
|
field_bytes: &self.all_bytes[named_arg.range()],
|
||||||
|
resolver: &self.resolver,
|
||||||
info: AnyExtrinsicCallFieldInfo::Current(ExtrinsicCallFieldInfo {
|
info: AnyExtrinsicCallFieldInfo::Current(ExtrinsicCallFieldInfo {
|
||||||
info: named_arg,
|
info: named_arg,
|
||||||
resolver: info.resolver,
|
resolver: info.resolver,
|
||||||
@@ -119,9 +132,10 @@ impl<'extrinsics, 'atblock> ExtrinsicCallFields<'extrinsics, 'atblock> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExtrinsicCallField<'extrinsics, 'atblock> {
|
pub struct ExtrinsicCallField<'fields, 'extrinsics, 'atblock> {
|
||||||
field_bytes: &'extrinsics [u8],
|
field_bytes: &'extrinsics [u8],
|
||||||
info: AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock>,
|
info: AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock>,
|
||||||
|
resolver: &'fields AnyResolver<'atblock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock> {
|
enum AnyExtrinsicCallFieldInfo<'extrinsics, 'atblock> {
|
||||||
@@ -144,7 +158,7 @@ macro_rules! with_call_field_info {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'extrinsics, 'atblock> ExtrinsicCallField<'extrinsics, 'atblock> {
|
impl<'fields, 'extrinsics, 'atblock> ExtrinsicCallField<'fields, 'extrinsics, 'atblock> {
|
||||||
/// Get the raw bytes for this field.
|
/// Get the raw bytes for this field.
|
||||||
pub fn bytes(&self) -> &'extrinsics [u8] {
|
pub fn bytes(&self) -> &'extrinsics [u8] {
|
||||||
self.field_bytes
|
self.field_bytes
|
||||||
@@ -155,6 +169,22 @@ impl<'extrinsics, 'atblock> ExtrinsicCallField<'extrinsics, 'atblock> {
|
|||||||
with_call_field_info!(&self.info => info.info.name())
|
with_call_field_info!(&self.info => info.info.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Visit the given field with a [`scale_decode::visitor::Visitor`]. This is like a lower level
|
||||||
|
/// version of [`ExtrinsicCallField::decode_as`], as the visitor is able to preserve lifetimes
|
||||||
|
/// and has access to more type information than is available via [`ExtrinsicCallField::decode_as`].
|
||||||
|
pub fn visit<V: scale_decode::visitor::Visitor<TypeResolver = AnyResolver<'atblock>>>(
|
||||||
|
&self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value<'extrinsics, 'fields>, V::Error> {
|
||||||
|
let type_id = match &self.info {
|
||||||
|
AnyExtrinsicCallFieldInfo::Current(info) => AnyTypeId::A(*info.info.ty()),
|
||||||
|
AnyExtrinsicCallFieldInfo::Legacy(info) => AnyTypeId::B(info.info.ty().clone()),
|
||||||
|
};
|
||||||
|
let cursor = &mut self.bytes();
|
||||||
|
|
||||||
|
scale_decode::visitor::decode_with_visitor(cursor, type_id, self.resolver, visitor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to decode the value of this field into the given type.
|
/// Attempt to decode the value of this field into the given type.
|
||||||
pub fn decode_as<T: scale_decode::DecodeAsType>(&self) -> Result<T, ExtrinsicCallError> {
|
pub fn decode_as<T: scale_decode::DecodeAsType>(&self) -> Result<T, ExtrinsicCallError> {
|
||||||
with_call_field_info!(&self.info => {
|
with_call_field_info!(&self.info => {
|
||||||
|
|||||||
@@ -110,6 +110,3 @@ impl<'extrinsics, 'atblock> Extrinsic<'extrinsics, 'atblock> {
|
|||||||
ExtrinsicTransactionParams::new(self.bytes, self.info)
|
ExtrinsicTransactionParams::new(self.bytes, self.info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add extrinsic.call() with .bytes, and .decode function to make it easy to decode call fields into Value or whatever.
|
|
||||||
// Then add this to the example. Make sure we can do everything that dot-block-decoder does easily.
|
|
||||||
|
|||||||
@@ -20,3 +20,8 @@ pub use error::Error;
|
|||||||
pub mod ext {
|
pub mod ext {
|
||||||
pub use futures::stream::{Stream, StreamExt};
|
pub use futures::stream::{Stream, StreamExt};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper types that could be useful.
|
||||||
|
pub mod helpers {
|
||||||
|
pub use crate::utils::{AnyResolver, AnyResolverError, AnyTypeId};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::storage_info::AnyStorageInfo;
|
use super::storage_info::AnyStorageInfo;
|
||||||
use super::storage_info::with_info;
|
use super::storage_info::with_info;
|
||||||
use crate::error::StorageValueError;
|
use crate::error::StorageValueError;
|
||||||
|
use crate::utils::{AnyResolver, AnyTypeId};
|
||||||
use scale_decode::DecodeAsType;
|
use scale_decode::DecodeAsType;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -9,12 +10,22 @@ use std::sync::Arc;
|
|||||||
pub struct StorageValue<'atblock> {
|
pub struct StorageValue<'atblock> {
|
||||||
pub(crate) info: Arc<AnyStorageInfo<'atblock>>,
|
pub(crate) info: Arc<AnyStorageInfo<'atblock>>,
|
||||||
bytes: Cow<'atblock, [u8]>,
|
bytes: Cow<'atblock, [u8]>,
|
||||||
|
resolver: AnyResolver<'atblock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'atblock> StorageValue<'atblock> {
|
impl<'atblock> StorageValue<'atblock> {
|
||||||
/// Create a new storage value.
|
/// Create a new storage value.
|
||||||
pub fn new(info: Arc<AnyStorageInfo<'atblock>>, bytes: Cow<'atblock, [u8]>) -> Self {
|
pub(crate) fn new(info: Arc<AnyStorageInfo<'atblock>>, bytes: Cow<'atblock, [u8]>) -> Self {
|
||||||
Self { info, bytes }
|
let resolver = match &*info {
|
||||||
|
AnyStorageInfo::Current(info) => AnyResolver::A(info.resolver),
|
||||||
|
AnyStorageInfo::Legacy(info) => AnyResolver::B(info.resolver),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
info,
|
||||||
|
bytes,
|
||||||
|
resolver,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the raw bytes for this storage value.
|
/// Get the raw bytes for this storage value.
|
||||||
@@ -27,6 +38,22 @@ impl<'atblock> StorageValue<'atblock> {
|
|||||||
self.bytes.to_vec()
|
self.bytes.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Visit the given field with a [`scale_decode::visitor::Visitor`]. This is like a lower level
|
||||||
|
/// version of [`StorageValue::decode_as`], as the visitor is able to preserve lifetimes
|
||||||
|
/// and has access to more type information than is available via [`StorageValue::decode_as`].
|
||||||
|
pub fn visit<V: scale_decode::visitor::Visitor<TypeResolver = AnyResolver<'atblock>>>(
|
||||||
|
&self,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value<'_, '_>, V::Error> {
|
||||||
|
let type_id = match &*self.info {
|
||||||
|
AnyStorageInfo::Current(info) => AnyTypeId::A(info.info.value_id),
|
||||||
|
AnyStorageInfo::Legacy(info) => AnyTypeId::B(info.info.value_id.clone()),
|
||||||
|
};
|
||||||
|
let cursor = &mut self.bytes();
|
||||||
|
|
||||||
|
scale_decode::visitor::decode_with_visitor(cursor, type_id, &self.resolver, visitor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode this storage value.
|
/// Decode this storage value.
|
||||||
pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
|
pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
|
||||||
with_info!(info = &*self.info => {
|
with_info!(info = &*self.info => {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
mod any_resolver;
|
||||||
mod either;
|
mod either;
|
||||||
mod range_map;
|
mod range_map;
|
||||||
|
|
||||||
|
pub use any_resolver::{AnyResolver, AnyResolverError, AnyTypeId};
|
||||||
pub use either::Either;
|
pub use either::Either;
|
||||||
pub use range_map::RangeMap;
|
pub use range_map::RangeMap;
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
use super::Either;
|
||||||
|
use scale_info_legacy::LookupName;
|
||||||
|
use scale_type_resolver::ResolvedTypeVisitor;
|
||||||
|
|
||||||
|
/// A type resolver which could either be for modern or historic resolving.
|
||||||
|
pub type AnyResolver<'resolver> = Either<
|
||||||
|
&'resolver scale_info::PortableRegistry,
|
||||||
|
&'resolver scale_info_legacy::TypeRegistrySet<'resolver>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// A type ID which is either a modern or historic ID.
|
||||||
|
pub type AnyTypeId = Either<u32, scale_info_legacy::LookupName>;
|
||||||
|
|
||||||
|
impl Default for AnyTypeId {
|
||||||
|
fn default() -> Self {
|
||||||
|
// Not a sensible default, but we don't need / can't provide a sensible one.
|
||||||
|
AnyTypeId::A(u32::MAX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u32> for AnyTypeId {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
AnyTypeId::A(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<LookupName> for AnyTypeId {
|
||||||
|
fn from(value: LookupName) -> Self {
|
||||||
|
AnyTypeId::B(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TryFrom<AnyTypeId> for u32 {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: AnyTypeId) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
AnyTypeId::A(v) => Ok(v),
|
||||||
|
AnyTypeId::B(_) => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TryFrom<AnyTypeId> for LookupName {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: AnyTypeId) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
AnyTypeId::A(_) => Err(()),
|
||||||
|
AnyTypeId::B(v) => Ok(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A resolve error that comes from using [`AnyResolver`] to resolve some [`AnyTypeId`] into a type.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum AnyResolverError {
|
||||||
|
#[error("got a {got} type ID but expected a {expected} type ID")]
|
||||||
|
TypeIdMismatch {
|
||||||
|
got: &'static str,
|
||||||
|
expected: &'static str,
|
||||||
|
},
|
||||||
|
#[error("{0}")]
|
||||||
|
ScaleInfo(scale_type_resolver::portable_registry::Error),
|
||||||
|
#[error("{0}")]
|
||||||
|
ScaleInfoLegacy(scale_info_legacy::type_registry::TypeRegistryResolveError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'resolver> scale_type_resolver::TypeResolver for AnyResolver<'resolver> {
|
||||||
|
type TypeId = AnyTypeId;
|
||||||
|
type Error = AnyResolverError;
|
||||||
|
|
||||||
|
fn resolve_type<'this, V: ResolvedTypeVisitor<'this, TypeId = Self::TypeId>>(
|
||||||
|
&'this self,
|
||||||
|
type_id: Self::TypeId,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error> {
|
||||||
|
match (self, type_id) {
|
||||||
|
(Either::A(resolver), Either::A(id)) => resolver
|
||||||
|
.resolve_type(id, ModernVisitor(visitor))
|
||||||
|
.map_err(AnyResolverError::ScaleInfo),
|
||||||
|
(Either::B(resolver), Either::B(id)) => resolver
|
||||||
|
.resolve_type(id, LegacyVisitor(visitor))
|
||||||
|
.map_err(AnyResolverError::ScaleInfoLegacy),
|
||||||
|
(Either::A(_), Either::B(_)) => Err(AnyResolverError::TypeIdMismatch {
|
||||||
|
got: "LookupName",
|
||||||
|
expected: "u32",
|
||||||
|
}),
|
||||||
|
(Either::B(_), Either::A(_)) => Err(AnyResolverError::TypeIdMismatch {
|
||||||
|
got: "u32",
|
||||||
|
expected: "LookupName",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to have a visitor which understands only modern or legacy types, and can wrap the more generic visitor
|
||||||
|
// that must be provided to AnyResolver::resolve_type. This then allows us to visit historic _or_ modern types
|
||||||
|
// using the single visitor provided by the user.
|
||||||
|
struct LegacyVisitor<V>(V);
|
||||||
|
struct ModernVisitor<V>(V);
|
||||||
|
|
||||||
|
mod impls {
|
||||||
|
use super::{AnyTypeId, LegacyVisitor, LookupName, ModernVisitor};
|
||||||
|
use scale_type_resolver::*;
|
||||||
|
|
||||||
|
// An ugly implementation which maps from modern or legacy types into our AnyTypeId,
|
||||||
|
// to make LegacyVisitor and ModernVisitor valid visitors when wrapping a generic "any" visitor.
|
||||||
|
macro_rules! impl_visitor_mapper {
|
||||||
|
($struc:ident, $type_id_ty:ident, $variant:ident) => {
|
||||||
|
impl<'this, V> ResolvedTypeVisitor<'this> for $struc<V>
|
||||||
|
where
|
||||||
|
V: ResolvedTypeVisitor<'this, TypeId = AnyTypeId>,
|
||||||
|
{
|
||||||
|
type TypeId = $type_id_ty;
|
||||||
|
type Value = V::Value;
|
||||||
|
|
||||||
|
fn visit_unhandled(self, kind: UnhandledKind) -> Self::Value {
|
||||||
|
self.0.visit_unhandled(kind)
|
||||||
|
}
|
||||||
|
fn visit_array(self, type_id: Self::TypeId, len: usize) -> Self::Value {
|
||||||
|
self.0.visit_array(AnyTypeId::$variant(type_id), len)
|
||||||
|
}
|
||||||
|
fn visit_not_found(self) -> Self::Value {
|
||||||
|
self.0.visit_not_found()
|
||||||
|
}
|
||||||
|
fn visit_composite<Path, Fields>(self, path: Path, fields: Fields) -> Self::Value
|
||||||
|
where
|
||||||
|
Path: PathIter<'this>,
|
||||||
|
Fields: FieldIter<'this, Self::TypeId>,
|
||||||
|
{
|
||||||
|
self.0.visit_composite(
|
||||||
|
path,
|
||||||
|
fields.map(|field| Field {
|
||||||
|
name: field.name,
|
||||||
|
id: AnyTypeId::$variant(field.id),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn visit_variant<Path, Fields, Var>(self, path: Path, variants: Var) -> Self::Value
|
||||||
|
where
|
||||||
|
Path: PathIter<'this>,
|
||||||
|
Fields: FieldIter<'this, Self::TypeId>,
|
||||||
|
Var: VariantIter<'this, Fields>,
|
||||||
|
{
|
||||||
|
self.0.visit_variant(
|
||||||
|
path,
|
||||||
|
variants.map(|variant| Variant {
|
||||||
|
index: variant.index,
|
||||||
|
name: variant.name,
|
||||||
|
fields: variant.fields.map(|field| Field {
|
||||||
|
name: field.name,
|
||||||
|
id: AnyTypeId::$variant(field.id),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn visit_sequence<Path>(self, path: Path, type_id: Self::TypeId) -> Self::Value
|
||||||
|
where
|
||||||
|
Path: PathIter<'this>,
|
||||||
|
{
|
||||||
|
self.0.visit_sequence(path, AnyTypeId::$variant(type_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_tuple<TypeIds>(self, type_ids: TypeIds) -> Self::Value
|
||||||
|
where
|
||||||
|
TypeIds: ExactSizeIterator<Item = Self::TypeId>,
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
.visit_tuple(type_ids.map(|id| AnyTypeId::$variant(id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_primitive(self, primitive: Primitive) -> Self::Value {
|
||||||
|
self.0.visit_primitive(primitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_compact(self, type_id: Self::TypeId) -> Self::Value {
|
||||||
|
self.0.visit_compact(AnyTypeId::$variant(type_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bit_sequence(
|
||||||
|
self,
|
||||||
|
store_format: BitsStoreFormat,
|
||||||
|
order_format: BitsOrderFormat,
|
||||||
|
) -> Self::Value {
|
||||||
|
self.0.visit_bit_sequence(store_format, order_format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_visitor_mapper!(ModernVisitor, u32, A);
|
||||||
|
impl_visitor_mapper!(LegacyVisitor, LookupName, B);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
macro_rules! either {
|
macro_rules! either {
|
||||||
($name:ident( $fst:ident, $($variant:ident),* )) => {
|
($name:ident( $fst:ident, $($variant:ident),* )) => {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum $name<$fst, $($variant),*> {
|
pub enum $name<$fst, $($variant),*> {
|
||||||
$fst($fst),
|
$fst($fst),
|
||||||
$($variant($variant),)*
|
$($variant($variant),)*
|
||||||
|
|||||||
+57
-2
@@ -374,6 +374,27 @@ impl Metadata {
|
|||||||
<Self as codec::Decode>::decode(&mut bytes)
|
<Self as codec::Decode>::decode(&mut bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert V16 metadata into [`Metadata`].
|
||||||
|
pub fn from_v16(
|
||||||
|
metadata: frame_metadata::v16::RuntimeMetadataV16,
|
||||||
|
) -> Result<Self, TryFromError> {
|
||||||
|
metadata.try_into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert V15 metadata into [`Metadata`].
|
||||||
|
pub fn from_v15(
|
||||||
|
metadata: frame_metadata::v15::RuntimeMetadataV15,
|
||||||
|
) -> Result<Self, TryFromError> {
|
||||||
|
metadata.try_into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert V14 metadata into [`Metadata`].
|
||||||
|
pub fn from_v14(
|
||||||
|
metadata: frame_metadata::v14::RuntimeMetadataV14,
|
||||||
|
) -> Result<Self, TryFromError> {
|
||||||
|
metadata.try_into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert V13 metadata into [`Metadata`], given the necessary extra type information.
|
/// Convert V13 metadata into [`Metadata`], given the necessary extra type information.
|
||||||
#[cfg(feature = "legacy")]
|
#[cfg(feature = "legacy")]
|
||||||
pub fn from_v13(
|
pub fn from_v13(
|
||||||
@@ -1222,6 +1243,38 @@ impl<'a> CustomValueMetadata<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode SCALE encoded metadata.
|
||||||
|
///
|
||||||
|
/// - The default assumption is that metadata is encoded as [`frame_metadata::RuntimeMetadataPrefixed`]. This is the
|
||||||
|
/// expected format that metadata is encoded into.
|
||||||
|
/// - if this fails, we also try to decode as [`frame_metadata::RuntimeMetadata`].
|
||||||
|
/// - If this all fails, we also try to decode as [`frame_metadata::OpaqueMetadata`].
|
||||||
|
pub fn decode_runtime_metadata(
|
||||||
|
input: &[u8],
|
||||||
|
) -> Result<frame_metadata::RuntimeMetadata, codec::Error> {
|
||||||
|
use codec::Decode;
|
||||||
|
|
||||||
|
let err = match frame_metadata::RuntimeMetadataPrefixed::decode(&mut &*input) {
|
||||||
|
Ok(md) => return Ok(md.1),
|
||||||
|
Err(e) => e,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(md) = frame_metadata::RuntimeMetadata::decode(&mut &*input) {
|
||||||
|
return Ok(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
// frame_metadata::OpaqueMetadata is a vec of bytes. If we can decode the length, AND
|
||||||
|
// the length definitely corresponds to the number of remaining bytes, then we try to
|
||||||
|
// decode the inner bytes.
|
||||||
|
if let Ok(len) = codec::Compact::<u64>::decode(&mut &*input) {
|
||||||
|
if input.len() == len.0 as usize {
|
||||||
|
return decode_runtime_metadata(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Support decoding metadata from the "wire" format directly into this.
|
// Support decoding metadata from the "wire" format directly into this.
|
||||||
// Errors may be lost in the case that the metadata content is somehow invalid.
|
// Errors may be lost in the case that the metadata content is somehow invalid.
|
||||||
impl codec::Decode for Metadata {
|
impl codec::Decode for Metadata {
|
||||||
@@ -1231,9 +1284,11 @@ impl codec::Decode for Metadata {
|
|||||||
frame_metadata::RuntimeMetadata::V14(md) => md.try_into(),
|
frame_metadata::RuntimeMetadata::V14(md) => md.try_into(),
|
||||||
frame_metadata::RuntimeMetadata::V15(md) => md.try_into(),
|
frame_metadata::RuntimeMetadata::V15(md) => md.try_into(),
|
||||||
frame_metadata::RuntimeMetadata::V16(md) => md.try_into(),
|
frame_metadata::RuntimeMetadata::V16(md) => md.try_into(),
|
||||||
_ => return Err("Cannot try_into() to Metadata: unsupported metadata version".into()),
|
_ => {
|
||||||
|
return Err("Metadata::decode failed: Cannot try_into() to Metadata: unsupported metadata version".into())
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
metadata.map_err(|_e| "Cannot try_into() to Metadata.".into())
|
metadata.map_err(|_| "Metadata::decode failed: Cannot try_into() to Metadata".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ async fn decode_block_mortality() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit Mortal:
|
// Explicit Mortal:
|
||||||
for for_n_blocks in [4, 16, 128] {
|
for for_n_blocks in [16, 64, 128] {
|
||||||
let tx = submit_extrinsic_and_get_it_back(
|
let tx = submit_extrinsic_and_get_it_back(
|
||||||
&api,
|
&api,
|
||||||
DefaultExtrinsicParamsBuilder::new().mortal(for_n_blocks),
|
DefaultExtrinsicParamsBuilder::new().mortal(for_n_blocks),
|
||||||
|
|||||||
Reference in New Issue
Block a user