From 3f79680b2c97ac3a30192f80961052b254c042f5 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 8 Dec 2024 13:20:24 +0100 Subject: [PATCH] fix: sequence arguments --- crates/pop-cli/src/commands/call/parachain.rs | 2 +- .../pop-parachains/src/call/metadata/mod.rs | 74 +++++++++++++++---- crates/pop-parachains/src/call/mod.rs | 36 ++++++--- 3 files changed, 88 insertions(+), 24 deletions(-) diff --git a/crates/pop-cli/src/commands/call/parachain.rs b/crates/pop-cli/src/commands/call/parachain.rs index 9cbc7621..5dd4a740 100644 --- a/crates/pop-cli/src/commands/call/parachain.rs +++ b/crates/pop-cli/src/commands/call/parachain.rs @@ -262,7 +262,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 diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5e6f80cf..50af4077 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -146,19 +146,31 @@ pub async fn find_extrinsic_by_name( /// Parses and processes raw string parameters for an extrinsic, mapping them to `Value` types. /// /// # Arguments +/// * `extrinsic`: The definition of the extrinsic, containing parameter metadata. /// * `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( + extrinsic: &Extrinsic, + raw_params: Vec, +) -> Result, Error> { + extrinsic + .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)] @@ -229,6 +241,24 @@ mod tests { #[tokio::test] async fn parse_extrinsic_arguments_works() -> Result<()> { + /// Helper function to create a `Param` with default values. + fn create_param( + name: &str, + type_name: &str, + is_sequence: bool, + is_variant: bool, + is_tuple: bool, + ) -> Param { + Param { + name: name.to_string(), + type_name: type_name.to_string(), + sub_params: vec![], + is_optional: false, + is_sequence, + is_variant, + is_tuple, + } + } // Values for testing from: https://docs.rs/scale-value/0.18.0/scale_value/stringify/fn.from_str.html // and https://docs.rs/scale-value/0.18.0/scale_value/stringify/fn.from_str_custom.html let args = [ @@ -254,8 +284,26 @@ mod tests { .into_iter() .map(|b| Value::u128(b as u128)) .collect(); + // Define mock extrinsic with parameters for testing. + let extrinsic = Extrinsic { + name: "test_extrinsic".to_string(), + docs: "Test extrinsic documentation".to_string(), + params: vec![ + create_param("param_1", "u128", false, false, false), + create_param("param_2", "i128", false, false, false), + create_param("param_3", "bool", false, false, false), + create_param("param_4", "char", false, false, false), + create_param("param_5", "string", false, false, false), + create_param("param_6", "composite", false, false, false), + create_param("param_7", "variant", false, true, false), + create_param("param_8", "bit_sequence", false, false, false), + create_param("param_9", "tuple", false, false, true), + create_param("param_10", "composite", false, false, false), + ], + is_supported: true, + }; assert_eq!( - parse_extrinsic_arguments(args).await?, + parse_extrinsic_arguments(&extrinsic, args).await?, [ Value::u128(1), Value::i128(-1), diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 5ca10cd0..9a9940c8 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,11 @@ 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, args).await?; + Ok(subxt::dynamic::tx(pallet_name, extrinsic.name.clone(), parsed_args)) } /// Signs and submits a given extrinsic. @@ -77,7 +77,7 @@ pub fn encode_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] @@ -92,11 +92,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, @@ -105,7 +110,7 @@ mod tests { // Valid parameters let extrinsic = construct_extrinsic( "Balances", - "transfer_allow_death", + &transfer_allow_death, vec![ "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string(), "100".to_string(), @@ -120,16 +125,27 @@ 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, "Balances", "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(()) } #[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\"))")