Skip to main content

pop_chains/call/metadata/
params.rs

1// SPDX-License-Identifier: GPL-3.0
2
3use crate::errors::Error;
4use pop_common::format_type;
5use scale_info::{Field, PortableRegistry, TypeDef, form::PortableForm};
6use subxt::Metadata;
7
8/// Describes a parameter of a dispatchable function.
9#[derive(Clone, Debug, Default, Eq, PartialEq)]
10pub struct Param {
11	/// The name of the parameter.
12	pub name: String,
13	/// The type of the parameter.
14	pub type_name: String,
15	/// Nested parameters for composite, variants, types or tuples.
16	pub sub_params: Vec<Param>,
17	/// Indicates if the parameter is optional (`Option<T>`).
18	pub is_optional: bool,
19	/// Indicates if the parameter is a Tuple.
20	pub is_tuple: bool,
21	/// Indicates if the parameter is a Variant.
22	pub is_variant: bool,
23	/// Indicates if the parameter is a Sequence.
24	pub is_sequence: bool,
25}
26
27/// Transforms a metadata field into its `Param` representation.
28///
29/// # Arguments
30/// * `metadata`: The chain metadata.
31/// * `field`: A parameter of a dispatchable function (as [Field]).
32pub fn field_to_param(metadata: &Metadata, field: &Field<PortableForm>) -> Result<Param, Error> {
33	let registry = metadata.types();
34	if let Some(name) = field.type_name.as_deref() &&
35		name.contains("RuntimeCall")
36	{
37		return Err(Error::CallableNotSupported);
38	}
39	let name = field.name.as_deref().unwrap_or("Unnamed"); //It can be unnamed field
40	type_to_param(name, registry, field.ty.id)
41}
42
43/// Converts a type's metadata into a `Param` representation.
44///
45/// # Arguments
46/// * `name`: The name of the parameter.
47/// * `registry`: Type registry containing all types used in the metadata.
48/// * `type_id`: The ID of the type to be converted.
49pub fn type_to_param(
50	name: &str,
51	registry: &PortableRegistry,
52	type_id: u32,
53) -> Result<Param, Error> {
54	let type_info = registry
55		.resolve(type_id)
56		.ok_or_else(|| Error::MetadataParsingError(name.to_string()))?;
57	// Check for unsupported `RuntimeCall` type
58	if type_info.path.segments.contains(&"RuntimeCall".to_string()) {
59		return Err(Error::CallableNotSupported);
60	}
61	for param in &type_info.type_params {
62		if param.name.contains("RuntimeCall") {
63			return Err(Error::CallableNotSupported);
64		}
65	}
66	if type_info.path.segments == ["Option"] {
67		if let Some(sub_type_id) = type_info.type_params.first().and_then(|param| param.ty) {
68			// Recursive for the sub parameters
69			let sub_param = type_to_param(name, registry, sub_type_id.id)?;
70			Ok(Param {
71				name: name.to_string(),
72				type_name: sub_param.type_name,
73				sub_params: sub_param.sub_params,
74				is_optional: true,
75				..Default::default()
76			})
77		} else {
78			Err(Error::MetadataParsingError(name.to_string()))
79		}
80	} else {
81		// Determine the formatted type name.
82		let type_name = format_type(type_info, registry);
83		match &type_info.type_def {
84			TypeDef::Primitive(_) | TypeDef::Array(_) | TypeDef::Compact(_) =>
85				Ok(Param { name: name.to_string(), type_name, ..Default::default() }),
86			TypeDef::Composite(composite) => {
87				let sub_params = composite
88					.fields
89					.iter()
90					.map(|field| {
91						// Recursive for the sub parameters of composite type.
92						type_to_param(field.name.as_deref().unwrap_or(name), registry, field.ty.id)
93					})
94					.collect::<Result<Vec<Param>, Error>>()?;
95
96				Ok(Param { name: name.to_string(), type_name, sub_params, ..Default::default() })
97			},
98			TypeDef::Variant(variant) => {
99				let variant_params = variant
100					.variants
101					.iter()
102					.map(|variant_param| {
103						let variant_sub_params = variant_param
104							.fields
105							.iter()
106							.map(|field| {
107								// Recursive for the sub parameters of variant type.
108								type_to_param(
109									field.name.as_deref().unwrap_or(&variant_param.name),
110									registry,
111									field.ty.id,
112								)
113							})
114							.collect::<Result<Vec<Param>, Error>>()?;
115						Ok(Param {
116							name: variant_param.name.clone(),
117							type_name: "".to_string(),
118							sub_params: variant_sub_params,
119							is_variant: true,
120							..Default::default()
121						})
122					})
123					.collect::<Result<Vec<Param>, Error>>()?;
124
125				Ok(Param {
126					name: name.to_string(),
127					type_name,
128					sub_params: variant_params,
129					is_variant: true,
130					..Default::default()
131				})
132			},
133			TypeDef::Sequence(_) => Ok(Param {
134				name: name.to_string(),
135				type_name,
136				is_sequence: true,
137				..Default::default()
138			}),
139			TypeDef::Tuple(tuple) => {
140				let sub_params = tuple
141					.fields
142					.iter()
143					.enumerate()
144					.map(|(index, field_id)| {
145						type_to_param(
146							&format!("Index {index} of the tuple {name}"),
147							registry,
148							field_id.id,
149						)
150					})
151					.collect::<Result<Vec<Param>, Error>>()?;
152
153				Ok(Param {
154					name: name.to_string(),
155					type_name,
156					sub_params,
157					is_tuple: true,
158					..Default::default()
159				})
160			},
161			_ => Err(Error::MetadataParsingError(name.to_string())),
162		}
163	}
164}