diff --git a/Cargo.lock b/Cargo.lock index 4d6d8bd7..6ff7331d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2917,6 +2917,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2925,12 +3043,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -3839,6 +3968,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -7021,6 +7156,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -7196,6 +7342,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -7629,12 +7785,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -7698,9 +7848,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -7714,6 +7864,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -8482,6 +8644,18 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -8526,6 +8700,30 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4524214bc4629eba08d78ceb1d6507070cc0bcbbed23af74e19e6e924a24cf" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -8547,6 +8745,27 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -8567,6 +8786,28 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "zip" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index e33ae6d5..30c181ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ toml = "0.5.0" # networking reqwest = { version = "0.12", features = ["json"] } tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } -url = "2.5" +url = "2.5.4" # contracts subxt-signer = { version = "0.37.0", features = ["subxt", "sr25519"] } diff --git a/crates/pop-cli/src/commands/call/parachain.rs b/crates/pop-cli/src/commands/call/parachain.rs index 9ed95da1..37e702c8 100644 --- a/crates/pop-cli/src/commands/call/parachain.rs +++ b/crates/pop-cli/src/commands/call/parachain.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 +use std::path::Path; + use crate::cli::{self, traits::*}; use anyhow::{anyhow, Result}; use clap::Args; @@ -13,6 +15,7 @@ use url::Url; const DEFAULT_URL: &str = "ws://localhost:9944/"; const DEFAULT_URI: &str = "//Alice"; +const ENCODED_CALL_DATA_MAX_LEN: usize = 500; // Maximum length of encoded call data to display. /// Command to execute extrinsics with configurable pallets, arguments, and signing options. #[derive(Args, Clone)] @@ -184,7 +187,7 @@ impl CallParachainCommand { } args } else { - self.args.clone() + self.expand_file_arguments()? }; // Resolve who is signing the extrinsic. @@ -258,6 +261,21 @@ impl CallParachainCommand { self.url.is_none() || self.suri.is_none() } + + /// Replaces file arguments with their contents, leaving other arguments unchanged. + fn expand_file_arguments(&self) -> Result> { + self.args + .iter() + .map(|arg| { + if std::fs::metadata(arg).map(|m| m.is_file()).unwrap_or(false) { + std::fs::read_to_string(arg) + .map_err(|err| anyhow!("Failed to read file {}", err.to_string())) + } else { + Ok(arg.clone()) + } + }) + .collect() + } } // Represents a chain, including its URL, client connection, and available pallets. @@ -299,7 +317,7 @@ impl CallParachain { ) -> Result { let tx = match construct_extrinsic( self.pallet.name.as_str(), - self.extrinsic.name.as_str(), + &self.extrinsic, self.args.clone(), ) .await @@ -309,7 +327,11 @@ impl CallParachain { return Err(anyhow!("Error: {}", e)); }, }; - cli.info(format!("Encoded call data: {}", encode_call_data(client, &tx)?))?; + let encoded_data = encode_call_data(client, &tx)?; + // If the encoded call data is too long, don't display it all. + if encoded_data.len() < ENCODED_CALL_DATA_MAX_LEN { + cli.info(format!("Encoded call data: {}", encode_call_data(client, &tx)?))?; + } Ok(tx) } @@ -346,7 +368,18 @@ impl CallParachain { full_message.push_str(&format!(" --pallet {}", self.pallet)); full_message.push_str(&format!(" --extrinsic {}", self.extrinsic)); if !self.args.is_empty() { - let args: Vec<_> = self.args.iter().map(|a| format!("\"{a}\"")).collect(); + let args: Vec<_> = self + .args + .iter() + .map(|a| { + // If the argument is too long, don't show it all, truncate it. + if a.len() > ENCODED_CALL_DATA_MAX_LEN { + format!("\"{}...{}\"", &a[..20], &a[a.len() - 20..]) + } else { + format!("\"{a}\"") + } + }) + .collect(); full_message.push_str(&format!(" --args {}", args.join(" "))); } full_message.push_str(&format!(" --url {} --suri {}", chain.url, self.suri)); @@ -402,7 +435,9 @@ fn prompt_for_param(cli: &mut impl Cli, param: &Param) -> Result { // Resolves the value of a parameter based on its type. fn get_param_value(cli: &mut impl Cli, param: &Param) -> Result { - if param.sub_params.is_empty() { + if param.is_sequence { + prompt_for_sequence_param(cli, param) + } else if param.sub_params.is_empty() { prompt_for_primitive_param(cli, param) } else if param.is_variant { prompt_for_variant_param(cli, param) @@ -413,6 +448,25 @@ fn get_param_value(cli: &mut impl Cli, param: &Param) -> Result { } } +// Prompt for the value when it is a sequence. +fn prompt_for_sequence_param(cli: &mut impl Cli, param: &Param) -> Result { + let input_value = cli + .input(format!( + "The value for `{}` might be too large to enter. You may enter the path to a file instead.", + param.name + )) + .placeholder(&format!( + "Enter a value of type {} or provide a file path (e.g., /path/to/your/file.json)", + param.type_name + )) + .interact()?; + if Path::new(&input_value).is_file() { + return std::fs::read_to_string(&input_value) + .map_err(|err| anyhow!("Failed to read file {}", err.to_string())); + } + Ok(input_value) +} + // Prompt for the value when it is a primitive. fn prompt_for_primitive_param(cli: &mut impl Cli, param: &Param) -> Result { Ok(cli @@ -507,6 +561,7 @@ fn parse_extrinsic_name(name: &str) -> Result { mod tests { use super::*; use crate::cli::MockCli; + use tempfile::tempdir; use url::Url; #[tokio::test] @@ -566,7 +621,7 @@ mod tests { ), 0, // "remark" extrinsic ) - .expect_input("Enter the value for the parameter: remark", "0x11".into()) + .expect_input("The value for `remark` might be too large to enter. You may enter the path to a file instead.", "0x11".into()) .expect_input("Signer of the extrinsic:", "//Bob".into()); let chain = call_config.configure_chain(&mut cli).await?; @@ -753,6 +808,42 @@ mod tests { Ok(()) } + #[test] + fn expand_file_arguments_works() -> Result<()> { + let mut call_config = CallParachainCommand { + pallet: Some("Registrar".to_string()), + extrinsic: Some("register".to_string()), + args: vec!["2000".to_string(), "0x1".to_string(), "0x12".to_string()].to_vec(), + url: Some(Url::parse("wss://rpc1.paseo.popnetwork.xyz")?), + suri: Some(DEFAULT_URI.to_string()), + skip_confirm: false, + }; + assert_eq!( + call_config.expand_file_arguments()?, + vec!["2000".to_string(), "0x1".to_string(), "0x12".to_string()] + ); + // Temporal file for testing when the input is a file. + let temp_dir = tempdir()?; + let genesis_file = temp_dir.path().join("genesis_file.json"); + std::fs::write(&genesis_file, "genesis_file_content")?; + let wasm_file = temp_dir.path().join("wasm_file.json"); + std::fs::write(&wasm_file, "wasm_file_content")?; + call_config.args = vec![ + "2000".to_string(), + genesis_file.display().to_string(), + wasm_file.display().to_string(), + ]; + assert_eq!( + call_config.expand_file_arguments()?, + vec![ + "2000".to_string(), + "genesis_file_content".to_string(), + "wasm_file_content".to_string() + ] + ); + Ok(()) + } + #[test] fn display_message_works() -> Result<()> { let mut cli = MockCli::new().expect_outro(&"Call completed successfully!"); @@ -869,6 +960,28 @@ mod tests { assert_eq!(params[0], "(0, 0)".to_string()); // task: test tuples assert_eq!(params[1], "0".to_string()); // retries: test primitive assert_eq!(params[2], "0".to_string()); // period: test primitive + cli.verify()?; + + // Using System remark extrinsic to test the sequence params + let extrinsic = find_extrinsic_by_name(&pallets, "System", "remark").await?; + // Temporal file for testing the input. + let temp_dir = tempdir()?; + let file = temp_dir.path().join("file.json"); + std::fs::write(&file, "testing")?; + + let mut cli = MockCli::new() + .expect_input( + "The value for `remark` might be too large to enter. You may enter the path to a file instead.", + file.display().to_string(), + ); + + // Test all the extrinsic params + let mut params: Vec = Vec::new(); + for param in extrinsic.params { + params.push(prompt_for_param(&mut cli, ¶m)?); + } + assert_eq!(params.len(), 1); + assert_eq!(params[0], "testing".to_string()); // remark: test sequence from file cli.verify() } diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 89edd5c2..d3a057d4 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -146,19 +146,30 @@ pub async fn find_extrinsic_by_name( /// Parses and processes raw string parameters for an extrinsic, mapping them to `Value` types. /// /// # Arguments +/// * `params`: The metadata definition for each parameter of the extrinsic. /// * `raw_params`: A vector of raw string arguments for the extrinsic. -pub async fn parse_extrinsic_arguments(raw_params: Vec) -> Result, Error> { - let mut parsed_params: Vec = Vec::new(); - for raw_param in raw_params { - let parsed_value: Value = scale_value::stringify::from_str_custom() - .add_custom_parser(custom_parsers::parse_hex) - .add_custom_parser(custom_parsers::parse_ss58) - .parse(&raw_param) - .0 - .map_err(|_| Error::ParamProcessingError)?; - parsed_params.push(parsed_value); - } - Ok(parsed_params) +pub async fn parse_extrinsic_arguments( + params: &[Param], + raw_params: Vec, +) -> Result, Error> { + params + .iter() + .zip(raw_params) + .map(|(param, raw_param)| { + // Convert sequence parameters to hex if is_sequence + let processed_param = if param.is_sequence && !raw_param.starts_with("0x") { + format!("0x{}", hex::encode(raw_param)) + } else { + raw_param + }; + scale_value::stringify::from_str_custom() + .add_custom_parser(custom_parsers::parse_hex) + .add_custom_parser(custom_parsers::parse_ss58) + .parse(&processed_param) + .0 + .map_err(|_| Error::ParamProcessingError) + }) + .collect() } #[cfg(test)] @@ -191,6 +202,7 @@ mod tests { assert!(!first_extrinsic.params.first().unwrap().is_optional); assert!(!first_extrinsic.params.first().unwrap().is_tuple); assert!(!first_extrinsic.params.first().unwrap().is_variant); + assert!(first_extrinsic.params.first().unwrap().is_sequence); Ok(()) } @@ -253,8 +265,21 @@ mod tests { .into_iter() .map(|b| Value::u128(b as u128)) .collect(); + // Define mock extrinsic parameters for testing. + let params = vec![ + Param { type_name: "u128".to_string(), ..Default::default() }, + Param { type_name: "i128".to_string(), ..Default::default() }, + Param { type_name: "bool".to_string(), ..Default::default() }, + Param { type_name: "char".to_string(), ..Default::default() }, + Param { type_name: "string".to_string(), ..Default::default() }, + Param { type_name: "compostie".to_string(), ..Default::default() }, + Param { type_name: "variant".to_string(), is_variant: true, ..Default::default() }, + Param { type_name: "bit_sequence".to_string(), ..Default::default() }, + Param { type_name: "tuple".to_string(), is_tuple: true, ..Default::default() }, + Param { type_name: "composite".to_string(), ..Default::default() }, + ]; assert_eq!( - parse_extrinsic_arguments(args).await?, + parse_extrinsic_arguments(¶ms, args).await?, [ Value::u128(1), Value::i128(-1), diff --git a/crates/pop-parachains/src/call/metadata/params.rs b/crates/pop-parachains/src/call/metadata/params.rs index 12fd7273..077d27dd 100644 --- a/crates/pop-parachains/src/call/metadata/params.rs +++ b/crates/pop-parachains/src/call/metadata/params.rs @@ -6,7 +6,7 @@ use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef}; use subxt::{Metadata, OnlineClient, SubstrateConfig}; /// Describes a parameter of an extrinsic. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Param { /// The name of the parameter. pub name: String, @@ -20,6 +20,8 @@ pub struct Param { pub is_tuple: bool, /// Indicates if the parameter is a Variant. pub is_variant: bool, + /// Indicates if the parameter is a Sequence. + pub is_sequence: bool, } /// Transforms a metadata field into its `Param` representation. @@ -66,6 +68,7 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res is_optional: true, is_tuple: false, is_variant: false, + is_sequence: false, }) } else { Err(Error::MetadataParsingError(name)) @@ -74,16 +77,14 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res // Determine the formatted type name. let type_name = format_type(type_info, registry); match &type_info.type_def { - TypeDef::Primitive(_) | - TypeDef::Array(_) | - TypeDef::Sequence(_) | - TypeDef::Compact(_) => Ok(Param { + TypeDef::Primitive(_) | TypeDef::Array(_) | TypeDef::Compact(_) => Ok(Param { name, type_name, sub_params: Vec::new(), is_optional: false, is_tuple: false, is_variant: false, + is_sequence: false, }), TypeDef::Composite(composite) => { let sub_params = composite @@ -106,6 +107,7 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res is_optional: false, is_tuple: false, is_variant: false, + is_sequence: false, }) }, TypeDef::Variant(variant) => { @@ -132,6 +134,7 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res is_optional: false, is_tuple: false, is_variant: true, + is_sequence: false, }) }) .collect::, Error>>()?; @@ -143,8 +146,18 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res is_optional: false, is_tuple: false, is_variant: true, + is_sequence: false, }) }, + TypeDef::Sequence(_) => Ok(Param { + name, + type_name, + sub_params: Vec::new(), + is_optional: false, + is_tuple: false, + is_variant: false, + is_sequence: true, + }), TypeDef::Tuple(tuple) => { let sub_params = tuple .fields @@ -166,6 +179,7 @@ fn type_to_param(name: String, registry: &PortableRegistry, type_id: u32) -> Res is_optional: false, is_tuple: true, is_variant: false, + is_sequence: false, }) }, _ => Err(Error::MetadataParsingError(name)), diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 2368c31f..971b88db 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::errors::Error; +use crate::{errors::Error, Extrinsic}; use pop_common::create_signer; use subxt::{ dynamic::Value, @@ -28,11 +28,12 @@ pub async fn set_up_client(url: &str) -> Result, E /// * `args` - A vector of string arguments to be passed to the extrinsic. pub async fn construct_extrinsic( pallet_name: &str, - extrinsic_name: &str, + extrinsic: &Extrinsic, args: Vec, ) -> Result { - let parsed_args: Vec = metadata::parse_extrinsic_arguments(args).await?; - Ok(subxt::dynamic::tx(pallet_name, extrinsic_name, parsed_args)) + let parsed_args: Vec = + metadata::parse_extrinsic_arguments(&extrinsic.params, args).await?; + Ok(subxt::dynamic::tx(pallet_name, extrinsic.name.clone(), parsed_args)) } /// Signs and submits a given extrinsic. @@ -125,7 +126,7 @@ pub async fn sign_and_submit_extrinsic_with_call_data( mod tests { use super::*; - use crate::set_up_client; + use crate::{find_extrinsic_by_name, parse_chain_metadata, set_up_client}; use anyhow::Result; #[tokio::test] @@ -140,11 +141,16 @@ mod tests { #[tokio::test] async fn construct_extrinsic_works() -> Result<()> { + let client = set_up_client("wss://rpc1.paseo.popnetwork.xyz").await?; + let pallets = parse_chain_metadata(&client).await?; + let transfer_allow_death = + find_extrinsic_by_name(&pallets, "Balances", "transfer_allow_death").await?; + // Wrong parameters assert!(matches!( construct_extrinsic( "Balances", - "transfer_allow_death", + &transfer_allow_death, vec!["Bob".to_string(), "100".to_string()], ) .await, @@ -153,7 +159,7 @@ mod tests { // Valid parameters let extrinsic = construct_extrinsic( "Balances", - "transfer_allow_death", + &transfer_allow_death, vec![ "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string(), "100".to_string(), @@ -168,8 +174,14 @@ mod tests { #[tokio::test] async fn encode_call_data_works() -> Result<()> { let client = set_up_client("wss://rpc1.paseo.popnetwork.xyz").await?; - let extrinsic = construct_extrinsic("System", "remark", vec!["0x11".to_string()]).await?; + let pallets = parse_chain_metadata(&client).await?; + let remark = find_extrinsic_by_name(&pallets, "System", "remark").await?; + let extrinsic = construct_extrinsic("System", &remark, vec!["0x11".to_string()]).await?; assert_eq!(encode_call_data(&client, &extrinsic)?, "0x00000411"); + let extrinsic = construct_extrinsic("System", &remark, vec!["123".to_string()]).await?; + assert_eq!(encode_call_data(&client, &extrinsic)?, "0x00000c313233"); + let extrinsic = construct_extrinsic("System", &remark, vec!["test".to_string()]).await?; + assert_eq!(encode_call_data(&client, &extrinsic)?, "0x00001074657374"); Ok(()) } @@ -186,8 +198,13 @@ mod tests { #[tokio::test] async fn sign_and_submit_wrong_extrinsic_fails() -> Result<()> { let client = set_up_client("wss://rpc1.paseo.popnetwork.xyz").await?; - let tx = - construct_extrinsic("WrongPallet", "wrongExtrinsic", vec!["0x11".to_string()]).await?; + let extrinsic = Extrinsic { + name: "wrong_extrinsic".to_string(), + docs: "documentation".to_string(), + params: vec![], + is_supported: true, + }; + let tx = construct_extrinsic("WrongPallet", &extrinsic, vec!["0x11".to_string()]).await?; assert!(matches!( sign_and_submit_extrinsic(client, tx, "//Alice").await, Err(Error::ExtrinsicSubmissionError(message)) if message.contains("PalletNameNotFound(\"WrongPallet\"))") diff --git a/deny.toml b/deny.toml index 5c295de5..53c6d37e 100644 --- a/deny.toml +++ b/deny.toml @@ -20,6 +20,7 @@ allow = [ "GPL-3.0", "MIT", "MPL-2.0", + "Unicode-3.0", "Unicode-DFS-2016", "Unlicense" ]