From 4d54652fc1f9c3f090e11a17211c512268c717ac Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 4 Nov 2024 18:26:58 +0100 Subject: [PATCH 01/13] fix: automatically add some or none to Option argument --- crates/pop-cli/src/commands/call/contract.rs | 4 +-- crates/pop-contracts/src/call/metadata.rs | 35 ++++++++++++++++++++ crates/pop-contracts/src/errors.rs | 2 ++ crates/pop-contracts/src/lib.rs | 2 +- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index 1a559ebc..fc9b7787 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -30,7 +30,7 @@ pub struct CallContractCommand { #[clap(long, short)] message: Option, /// The constructor arguments, encoded as strings. - #[clap(long, num_args = 0..)] + #[clap(long, num_args = 0.., value_delimiter = ',')] args: Vec, /// The value to be transferred as part of the call. #[clap(name = "value", short = 'v', long, default_value = DEFAULT_PAYABLE_VALUE)] @@ -96,7 +96,7 @@ impl CallContractCommand { full_message.push_str(&format!(" --message {}", message)); } if !self.args.is_empty() { - full_message.push_str(&format!(" --args {}", self.args.join(" "))); + full_message.push_str(&format!(" --args {}", self.args.join(","))); } if self.value != DEFAULT_PAYABLE_VALUE { full_message.push_str(&format!(" --value {}", self.value)); diff --git a/crates/pop-contracts/src/call/metadata.rs b/crates/pop-contracts/src/call/metadata.rs index 605d0620..31c2a07d 100644 --- a/crates/pop-contracts/src/call/metadata.rs +++ b/crates/pop-contracts/src/call/metadata.rs @@ -56,6 +56,19 @@ pub fn get_messages(path: &Path) -> Result, Error> { } Ok(messages) } + +/// Extracts the information of a smart contract message parsing the metadata file. +/// +/// # Arguments +/// * `path` - Location path of the project. +/// * `message` - The label of the contract message. +pub fn get_message(path: &Path, message: &str) -> Result { + get_messages(path)? + .into_iter() + .find(|msg| msg.label == message) + .ok_or_else(|| Error::InvalidMessageName(message.to_string())) +} + // Parse the message parameters into a vector of argument labels. fn process_args(message_params: &[MessageParamSpec]) -> Vec { let mut args: Vec = Vec::new(); @@ -99,4 +112,26 @@ mod tests { assert_eq!(message[2].args[0].type_name, "bool".to_string()); Ok(()) } + + #[test] + fn get_message_work() -> Result<()> { + let temp_dir = generate_smart_contract_test_environment()?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + assert!(matches!( + get_message(&temp_dir.path().join("testing"), "wrong_flip"), + Err(Error::InvalidMessageName(name)) if name == "wrong_flip".to_string())); + let message = get_message(&temp_dir.path().join("testing"), "specific_flip")?; + assert_eq!(message.label, "specific_flip"); + assert_eq!(message.docs, " A message for testing, flips the value of the stored `bool` with `new_value` and is payable"); + // assert parsed arguments + assert_eq!(message.args.len(), 1); + assert_eq!(message.args[0].label, "new_value".to_string()); + assert_eq!(message.args[0].type_name, "bool".to_string()); + Ok(()) + } } diff --git a/crates/pop-contracts/src/errors.rs b/crates/pop-contracts/src/errors.rs index a0ea14ad..066e35e1 100644 --- a/crates/pop-contracts/src/errors.rs +++ b/crates/pop-contracts/src/errors.rs @@ -28,6 +28,8 @@ pub enum Error { InstallContractsNode(String), #[error("{0}")] InstantiateContractError(String), + #[error("Invalid message name: {0}")] + InvalidMessageName(String), #[error("Invalid name: {0}")] InvalidName(String), #[error("IO error: {0}")] diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index a3ff0175..521833be 100644 --- a/crates/pop-contracts/src/lib.rs +++ b/crates/pop-contracts/src/lib.rs @@ -15,7 +15,7 @@ mod utils; pub use build::{build_smart_contract, is_supported, Verbosity}; pub use call::{ call_smart_contract, dry_run_call, dry_run_gas_estimate_call, - metadata::{get_messages, Message}, + metadata::{get_message, get_messages, Message}, set_up_call, CallOpts, }; pub use new::{create_smart_contract, is_valid_contract_name}; From 781ebc7a273c5ddfc8f0c50dd2e59aba5878ce26 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 4 Nov 2024 20:25:36 +0100 Subject: [PATCH 02/13] test: refactor and tests --- crates/pop-cli/src/commands/call/contract.rs | 8 +- crates/pop-contracts/src/call/metadata.rs | 84 ++++++++++- crates/pop-contracts/src/call/mod.rs | 26 ++-- crates/pop-contracts/src/errors.rs | 8 +- crates/pop-contracts/src/lib.rs | 2 +- .../tests/files/testing.contract | 2 +- crates/pop-contracts/tests/files/testing.json | 140 ++++++++++++------ 7 files changed, 206 insertions(+), 64 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index fc9b7787..3b22d014 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -671,6 +671,7 @@ mod tests { .expect_input("Enter the proof size limit:", "".into()) // Only if call .expect_input("Enter the gas limit:", "".into()) // Only if call .expect_input("Value to transfer to the call:", "50".into()) // Only if payable + .expect_input("Enter the value for the parameter: number", "2".into()) // Args for specific_flip .expect_input("Enter the value for the parameter: new_value", "true".into()) // Args for specific_flip .expect_select::( "Select the message to call:", @@ -691,7 +692,7 @@ mod tests { "Where is your project located?", temp_dir.path().join("testing").display().to_string(), ).expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -715,8 +716,9 @@ mod tests { Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()) ); assert_eq!(call_config.message, Some("specific_flip".to_string())); - assert_eq!(call_config.args.len(), 1); + assert_eq!(call_config.args.len(), 2); assert_eq!(call_config.args[0], "true".to_string()); + assert_eq!(call_config.args[1], "2".to_string()); assert_eq!(call_config.value, "50".to_string()); assert_eq!(call_config.gas_limit, None); assert_eq!(call_config.proof_size, None); @@ -725,7 +727,7 @@ mod tests { assert!(call_config.execute); assert!(!call_config.dry_run); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); diff --git a/crates/pop-contracts/src/call/metadata.rs b/crates/pop-contracts/src/call/metadata.rs index 31c2a07d..6c4b58db 100644 --- a/crates/pop-contracts/src/call/metadata.rs +++ b/crates/pop-contracts/src/call/metadata.rs @@ -62,7 +62,7 @@ pub fn get_messages(path: &Path) -> Result, Error> { /// # Arguments /// * `path` - Location path of the project. /// * `message` - The label of the contract message. -pub fn get_message(path: &Path, message: &str) -> Result { +fn get_message(path: &Path, message: &str) -> Result { get_messages(path)? .into_iter() .find(|msg| msg.label == message) @@ -81,6 +81,41 @@ fn process_args(message_params: &[MessageParamSpec]) -> Vec args } +/// Generates a list of processed argument values for a specified contract message, +/// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional +/// +/// # Arguments +/// * `path` - Location path of the project. +/// * `message_label` - Label of the contract message to retrieve. +/// * `args` - Argument values provided by the user. +pub fn generate_message_args( + path: Option<&Path>, + message_label: &str, + args: Vec, +) -> Result, Error> { + let contract_path = path.unwrap_or_else(|| Path::new("./")); + let message = get_message(&contract_path, message_label)?; + if args.len() != message.args.len() { + return Err(Error::WrongNumberArguments { + expected: message.args.len(), + provided: args.len(), + }); + } + Ok(args + .into_iter() + .zip(&message.args) + .map(|(arg, param)| { + if param.type_name == "Option" && arg.is_empty() { + "None".to_string() + } else if param.type_name == "Option" { + format!("Some({})", arg) + } else { + arg + } + }) + .collect::>()) +} + #[cfg(test)] mod tests { use std::env; @@ -107,9 +142,11 @@ mod tests { assert_eq!(message[2].label, "specific_flip"); assert_eq!(message[2].docs, " A message for testing, flips the value of the stored `bool` with `new_value` and is payable"); // assert parsed arguments - assert_eq!(message[2].args.len(), 1); + assert_eq!(message[2].args.len(), 2); assert_eq!(message[2].args[0].label, "new_value".to_string()); assert_eq!(message[2].args[0].type_name, "bool".to_string()); + assert_eq!(message[2].args[1].label, "number".to_string()); + assert_eq!(message[2].args[1].type_name, "Option".to_string()); Ok(()) } @@ -129,9 +166,50 @@ mod tests { assert_eq!(message.label, "specific_flip"); assert_eq!(message.docs, " A message for testing, flips the value of the stored `bool` with `new_value` and is payable"); // assert parsed arguments - assert_eq!(message.args.len(), 1); + assert_eq!(message.args.len(), 2); assert_eq!(message.args[0].label, "new_value".to_string()); assert_eq!(message.args[0].type_name, "bool".to_string()); + assert_eq!(message.args[1].label, "number".to_string()); + assert_eq!(message.args[1].type_name, "Option".to_string()); + Ok(()) + } + + #[test] + fn generate_message_args_work() -> Result<()> { + let temp_dir = generate_smart_contract_test_environment()?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + assert!(matches!( + generate_message_args(Some(&temp_dir.path().join("testing")),"wrong_flip", Vec::new()), + Err(Error::InvalidMessageName(error)) if error == "wrong_flip".to_string())); + assert!(matches!( + generate_message_args( + Some(&temp_dir.path().join("testing")), + "specific_flip", + Vec::new() + ), + Err(Error::WrongNumberArguments {expected, provided }) if expected == 2 && provided == 0 + )); + assert_eq!( + generate_message_args( + Some(&temp_dir.path().join("testing")), + "specific_flip", + ["true".to_string(), "2".to_string()].to_vec() + )?, + ["true".to_string(), "Some(2)".to_string()] + ); + assert_eq!( + generate_message_args( + Some(&temp_dir.path().join("testing")), + "specific_flip", + ["true".to_string(), "".to_string()].to_vec() + )?, + ["true".to_string(), "None".to_string()] + ); Ok(()) } } diff --git a/crates/pop-contracts/src/call/mod.rs b/crates/pop-contracts/src/call/mod.rs index 2c363301..6683541e 100644 --- a/crates/pop-contracts/src/call/mod.rs +++ b/crates/pop-contracts/src/call/mod.rs @@ -53,7 +53,7 @@ pub struct CallOpts { /// * `call_opts` - options for the `call` command. pub async fn set_up_call( call_opts: CallOpts, -) -> anyhow::Result> { +) -> Result, Error> { let token_metadata = TokenMetadata::query::(&call_opts.url).await?; let manifest_path = get_manifest_path(call_opts.path.as_deref())?; let signer = create_signer(&call_opts.suri)?; @@ -67,10 +67,16 @@ pub async fn set_up_call( parse_balance(&call_opts.value)?; let contract: ::AccountId = parse_account(&call_opts.contract)?; + // Parse the argument values input by the user. + let args = metadata::generate_message_args( + call_opts.path.as_deref(), + &call_opts.message, + call_opts.args, + )?; let call_exec: CallExec = CallCommandBuilder::new(contract.clone(), &call_opts.message, extrinsic_opts) - .args(call_opts.args.clone()) + .args(args) .value(value.denominate_balance(&token_metadata)?) .gas_limit(call_opts.gas_limit) .proof_size(call_opts.proof_size) @@ -212,11 +218,9 @@ mod tests { suri: "//Alice".to_string(), execute: false, }; - let call = set_up_call(call_opts).await; - assert!(call.is_err()); - let error = call.err().unwrap(); - assert_eq!(error.root_cause().to_string(), "Failed to find any contract artifacts in target directory. \nRun `cargo contract build --release` to generate the artifacts."); - + assert!( + matches!(set_up_call(call_opts).await, Err(Error::AnyhowError(message)) if message.root_cause().to_string() == "Failed to find any contract artifacts in target directory. \nRun `cargo contract build --release` to generate the artifacts.") + ); Ok(()) } #[tokio::test] @@ -233,11 +237,9 @@ mod tests { suri: "//Alice".to_string(), execute: false, }; - let call = set_up_call(call_opts).await; - assert!(call.is_err()); - let error = call.err().unwrap(); - assert_eq!(error.root_cause().to_string(), "No 'ink' dependency found"); - + assert!( + matches!(set_up_call(call_opts).await, Err(Error::AnyhowError(message)) if message.root_cause().to_string() == "No 'ink' dependency found") + ); Ok(()) } diff --git a/crates/pop-contracts/src/errors.rs b/crates/pop-contracts/src/errors.rs index 066e35e1..a22a0dcb 100644 --- a/crates/pop-contracts/src/errors.rs +++ b/crates/pop-contracts/src/errors.rs @@ -38,6 +38,8 @@ pub enum Error { KeyPairCreation(String), #[error("Failed to get manifest path: {0}")] ManifestPath(String), + #[error("Argument {0} is required")] + MissingArgument(String), #[error("Failed to create new contract project: {0}")] NewContract(String), #[error("ParseError error: {0}")] @@ -46,12 +48,14 @@ pub enum Error { ParseSecretURI(String), #[error("The `Repository` property is missing from the template variant")] RepositoryMissing, + #[error("Sourcing error {0}")] + SourcingError(SourcingError), #[error("Failed to execute test command: {0}")] TestCommand(String), #[error("Unsupported platform: {os}")] UnsupportedPlatform { os: &'static str }, #[error("{0}")] UploadContractError(String), - #[error("Sourcing error {0}")] - SourcingError(SourcingError), + #[error("Wrong number of arguments provided. Expecting {expected}, {provided} provided")] + WrongNumberArguments { expected: usize, provided: usize }, } diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index 521833be..a3ff0175 100644 --- a/crates/pop-contracts/src/lib.rs +++ b/crates/pop-contracts/src/lib.rs @@ -15,7 +15,7 @@ mod utils; pub use build::{build_smart_contract, is_supported, Verbosity}; pub use call::{ call_smart_contract, dry_run_call, dry_run_gas_estimate_call, - metadata::{get_message, get_messages, Message}, + metadata::{get_messages, Message}, set_up_call, CallOpts, }; pub use new::{create_smart_contract, is_valid_contract_name}; diff --git a/crates/pop-contracts/tests/files/testing.contract b/crates/pop-contracts/tests/files/testing.contract index 5fb54f3c..73ff479a 100644 --- a/crates/pop-contracts/tests/files/testing.contract +++ b/crates/pop-contracts/tests/files/testing.contract @@ -1 +1 @@ -{"source":{"hash":"0x80776e58b218850d7d86447b2edea78d827ed0ed2499ff3a92b7ea10e4f95eb5","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d01000000012b0860027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f0060017f006000017f60017f017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f72790201021003100f0101010105040006070002050002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0012066465706c6f7900130aa80c0f2b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0b2601017f230041106b220124002001410036020c20002001410c6a4104100a200141106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100a200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b3f01017f2000280204220145044041020f0b2000200141016b36020420002000280200220041016a3602004101410220002d000022004101461b410020001b0b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031011000b12004180800441003b0100410041021011000b8a0101057f230041106b22012400200142808001370208200141808004360204200141046a22041009024020012802082205200128020c2202490d00200128020421032001410036020c2001200520026b3602082001200220036a36020420002004100b200128020c220020012802084b0d00200320022001280204200010021a200141106a24000f0b000b0d0020004180800420011003000b990401067f230041106b2200240020004180800136020441808004200041046a10010240024020002802042202418180014f0d000240024020024104490d002000418480043602042000200241046b360208418380042d00002101418280042d00002104418180042d00002103418080042d00002202412f470440200241ec00470440200241e300470d02410221022003413a46200441a5014671200141d10046710d030c020b2003410f472004411d4772200141f70147720d01200041046a100d220241ff01714102460d010c020b41032102200341860146200441db004671200141d90146710d010b41014101100e000b200042808001370208200041808004360204200041046a2204100920002802082205200028020c2201490d00200028020421032000200520016b220536020420032001200120036a2201200410002000280204220320054b720d0020002003360208200020013602042004100d220141ff01714102460d0020002802080d000240024002404102200241026b41ff01712200200041024f1b41016b0e020100020b2002410171101041004100100e000b100c41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200141ff0171410047200041046a100b200028020c2200418180014f0440000b410020001011000b100c41ff01714105460d010b000b200141ff017145101041004100100e000be50101057f230041106b2200240002400240100c41ff01714105470d0020004180800136020c418080042000410c6a1001200028020c2201418180014f0d0020014104490d012000418480043602042000200141046b360208418380042d00002101418280042d00002102418180042d000021030240418080042d0000220441ed014704402004419b0147200341ae0147722002419d0147200141de004772720d03200041046a100d220041ff01714102470d010c030b200341cb00462002419d0146712001411b4671450d0241001010100f000b20001010100f000b000b41014101100e000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"}]}}}},{"id":2,"type":{"path":["Result"],"params":[{"name":"T","type":3},{"name":"E","type":4}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":3}],"index":0},{"name":"Err","fields":[{"type":4}],"index":1}]}}}},{"id":3,"type":{"def":{"tuple":[]}}},{"id":4,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":5,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":4}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":4}],"index":1}]}}}},{"id":6,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":7,"typeName":"[u8; 32]"}]}}}},{"id":7,"type":{"def":{"array":{"len":32,"type":8}}}},{"id":8,"type":{"def":{"primitive":"u8"}}},{"id":9,"type":{"def":{"primitive":"u128"}}},{"id":10,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":7,"typeName":"[u8; 32]"}]}}}},{"id":11,"type":{"def":{"primitive":"u64"}}},{"id":12,"type":{"def":{"primitive":"u32"}}},{"id":13,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}}]}},"ty":1}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}}],"returnType":{"type":2,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[],"returnType":{"type":2,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":2,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":5,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}}],"returnType":{"type":2,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":4,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":6,"displayName":["AccountId"]},"balance":{"type":9,"displayName":["Balance"]},"hash":{"type":10,"displayName":["Hash"]},"timestamp":{"type":11,"displayName":["Timestamp"]},"blockNumber":{"type":12,"displayName":["BlockNumber"]},"chainExtension":{"type":13,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file +{"source":{"hash":"0xd35166baa65bf10eb07c3d30c052d0fe752d6b98a864b61062e189be1a1ed550","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d0100000001270760027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f006000017f60017f017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f727902010210031110010101010004000506020000000002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0013066465706c6f7900140aff0d102b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0b2601017f230041106b220224002002200036020c20012002410c6a4104100a200241106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100a200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b7301037f230041106b22012400200141086a220320002802042202047f2000200241016b36020420002000280200220041016a36020020002d00000520000b3a000120032002453a000020012d0009210020012d00082102200141106a240041024101410220004101461b410020001b20021b0b12004180800441003b0100410041021011000b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031011000b920101057f230041106b220224002002428080013702082002418080043602044100200241046a22041009024020022802082206200228020c2203490d00200228020421052002410036020c2002200620036b3602082002200320056a36020420012004100b200020041009200228020c220020022802084b0d00200520032002280204200010021a200241106a24000f0b000b0d0020004180800420011003000b08002000200110100ba70501087f230041206b2200240020004180800136021441808004200041146a100102400240024020002802142202418180014f0d004104210541042106024020024104490d0020004184800436020c2000200241046b360210418380042d00002101418280042d00002104418180042d0000210302400240418080042d00002202412f470440200241ec00460d01200241e300472003413a4772200441a50147200141d1004772720d03410221010c020b200341860147200441db004772200141d90147720d02410321010c010b2003410f472004411d4772200141f70147720d012000410c6a100d220241ff01714102460d0120002802102204450d010240200028020c22032d000022010e020100020b20044105490d0120032800012106410121010b20002001360218200020023a0014410821050b200041146a220220056a2006360200200028021822044104460d01200028021c210520002d0014210620004280800137021820004180800436021441002002100920002802182207200028021c2201490d00200028021421032000200720016b220736021420032001200120036a2201200210002000280214220320074b720d0020002003360218200020013602142002100d220241ff01714102460d002000280218220141034d2001410447720d00200028021428000021000240024002404102200441026b2201200141024f1b41016b0e020100020b2005200020041b2006410047101241004100100f000b100c41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200241ff0171410047200041046a100b200028020c2200418180014f0440000b410020001011000b100c41ff01714105460d020b000b41014101100f000b2000200241ff017145101241004100100f000be90101057f230041106b2200240002400240100c41ff01714105470d0020004180800136020c418080042000410c6a1001200028020c2201418180014f0d0020014104490d012000418480043602042000200141046b360208418380042d00002101418280042d00002102418180042d000021030240418080042d0000220441ed014704402004419b0147200341ae0147722002419d0147200141de004772720d03200041046a100d220041ff01714102470d010c030b200341cb00462002419d0146712001411b4671450d02410041001010100e000b410020001010100e000b000b41014101100f000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"primitive":"u32"}}},{"id":2,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"},{"name":"number","type":1,"typeName":",>>::Type"}]}}}},{"id":3,"type":{"path":["Result"],"params":[{"name":"T","type":4},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":4}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":4,"type":{"def":{"tuple":[]}}},{"id":5,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":6,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":7,"type":{"path":["Option"],"params":[{"name":"T","type":1}],"def":{"variant":{"variants":[{"name":"None","index":0},{"name":"Some","fields":[{"type":1}],"index":1}]}}}},{"id":8,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":9,"type":{"def":{"array":{"len":32,"type":10}}}},{"id":10,"type":{"def":{"primitive":"u8"}}},{"id":11,"type":{"def":{"primitive":"u128"}}},{"id":12,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":13,"type":{"def":{"primitive":"u64"}}},{"id":14,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}},{"name":"number","layout":{"leaf":{"key":"0x00000000","ty":1}}}]}},"ty":2}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}}],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":6,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":7,"displayName":["Option"]}}],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":5,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":8,"displayName":["AccountId"]},"balance":{"type":11,"displayName":["Balance"]},"hash":{"type":12,"displayName":["Hash"]},"timestamp":{"type":13,"displayName":["Timestamp"]},"blockNumber":{"type":1,"displayName":["BlockNumber"]},"chainExtension":{"type":14,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file diff --git a/crates/pop-contracts/tests/files/testing.json b/crates/pop-contracts/tests/files/testing.json index ed230c98..8f28780d 100644 --- a/crates/pop-contracts/tests/files/testing.json +++ b/crates/pop-contracts/tests/files/testing.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0x80776e58b218850d7d86447b2edea78d827ed0ed2499ff3a92b7ea10e4f95eb5", + "hash": "0xd35166baa65bf10eb07c3d30c052d0fe752d6b98a864b61062e189be1a1ed550", "language": "ink! 5.0.0", "compiler": "rustc 1.78.0", "build_info": { @@ -33,6 +33,14 @@ }, { "id": 1, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 2, "type": { "path": [ "testing", @@ -46,6 +54,11 @@ "name": "value", "type": 0, "typeName": ",>>::Type" + }, + { + "name": "number", + "type": 1, + "typeName": ",>>::Type" } ] } @@ -53,7 +66,7 @@ } }, { - "id": 2, + "id": 3, "type": { "path": [ "Result" @@ -61,11 +74,11 @@ "params": [ { "name": "T", - "type": 3 + "type": 4 }, { "name": "E", - "type": 4 + "type": 5 } ], "def": { @@ -75,7 +88,7 @@ "name": "Ok", "fields": [ { - "type": 3 + "type": 4 } ], "index": 0 @@ -84,7 +97,7 @@ "name": "Err", "fields": [ { - "type": 4 + "type": 5 } ], "index": 1 @@ -95,7 +108,7 @@ } }, { - "id": 3, + "id": 4, "type": { "def": { "tuple": [] @@ -103,7 +116,7 @@ } }, { - "id": 4, + "id": 5, "type": { "path": [ "ink_primitives", @@ -122,7 +135,7 @@ } }, { - "id": 5, + "id": 6, "type": { "path": [ "Result" @@ -134,7 +147,7 @@ }, { "name": "E", - "type": 4 + "type": 5 } ], "def": { @@ -153,7 +166,7 @@ "name": "Err", "fields": [ { - "type": 4 + "type": 5 } ], "index": 1 @@ -164,7 +177,40 @@ } }, { - "id": 6, + "id": 7, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 1 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "index": 0 + }, + { + "name": "Some", + "fields": [ + { + "type": 1 + } + ], + "index": 1 + } + ] + } + } + } + }, + { + "id": 8, "type": { "path": [ "ink_primitives", @@ -175,7 +221,7 @@ "composite": { "fields": [ { - "type": 7, + "type": 9, "typeName": "[u8; 32]" } ] @@ -184,18 +230,18 @@ } }, { - "id": 7, + "id": 9, "type": { "def": { "array": { "len": 32, - "type": 8 + "type": 10 } } } }, { - "id": 8, + "id": 10, "type": { "def": { "primitive": "u8" @@ -203,7 +249,7 @@ } }, { - "id": 9, + "id": 11, "type": { "def": { "primitive": "u128" @@ -211,7 +257,7 @@ } }, { - "id": 10, + "id": 12, "type": { "path": [ "ink_primitives", @@ -222,7 +268,7 @@ "composite": { "fields": [ { - "type": 7, + "type": 9, "typeName": "[u8; 32]" } ] @@ -231,7 +277,7 @@ } }, { - "id": 11, + "id": 13, "type": { "def": { "primitive": "u64" @@ -239,15 +285,7 @@ } }, { - "id": 12, - "type": { - "def": { - "primitive": "u32" - } - } - }, - { - "id": 13, + "id": 14, "type": { "path": [ "ink_env", @@ -275,11 +313,20 @@ "ty": 0 } } + }, + { + "name": "number", + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 1 + } + } } ] } }, - "ty": 1 + "ty": 2 } }, "spec": { @@ -300,7 +347,7 @@ } ], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink_primitives", "ConstructorResult" @@ -317,7 +364,7 @@ "payable": false, "args": [], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink_primitives", "ConstructorResult" @@ -339,7 +386,7 @@ "payable": false, "args": [], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -359,7 +406,7 @@ "payable": false, "args": [], "returnType": { - "type": 5, + "type": 6, "displayName": [ "ink", "MessageResult" @@ -384,10 +431,19 @@ "bool" ] } + }, + { + "label": "number", + "type": { + "type": 7, + "displayName": [ + "Option" + ] + } } ], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -403,7 +459,7 @@ "events": [], "docs": [], "lang_error": { - "type": 4, + "type": 5, "displayName": [ "ink", "LangError" @@ -411,37 +467,37 @@ }, "environment": { "accountId": { - "type": 6, + "type": 8, "displayName": [ "AccountId" ] }, "balance": { - "type": 9, + "type": 11, "displayName": [ "Balance" ] }, "hash": { - "type": 10, + "type": 12, "displayName": [ "Hash" ] }, "timestamp": { - "type": 11, + "type": 13, "displayName": [ "Timestamp" ] }, "blockNumber": { - "type": 12, + "type": 1, "displayName": [ "BlockNumber" ] }, "chainExtension": { - "type": 13, + "type": 14, "displayName": [ "ChainExtension" ] From d361160418ce5a8d3863044540ae58500750c1e9 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 5 Nov 2024 09:04:57 +0100 Subject: [PATCH 03/13] refactor: improve code and comments --- crates/pop-contracts/src/call/metadata.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/pop-contracts/src/call/metadata.rs b/crates/pop-contracts/src/call/metadata.rs index 6c4b58db..76737a0a 100644 --- a/crates/pop-contracts/src/call/metadata.rs +++ b/crates/pop-contracts/src/call/metadata.rs @@ -94,7 +94,7 @@ pub fn generate_message_args( args: Vec, ) -> Result, Error> { let contract_path = path.unwrap_or_else(|| Path::new("./")); - let message = get_message(&contract_path, message_label)?; + let message = get_message(contract_path, message_label)?; if args.len() != message.args.len() { return Err(Error::WrongNumberArguments { expected: message.args.len(), @@ -104,14 +104,12 @@ pub fn generate_message_args( Ok(args .into_iter() .zip(&message.args) - .map(|(arg, param)| { - if param.type_name == "Option" && arg.is_empty() { - "None".to_string() - } else if param.type_name == "Option" { - format!("Some({})", arg) - } else { - arg - } + .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { + ("Option", true) => "None".to_string(), /* If the argument is Option and empty, + * replace it with `None` */ + ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not + * empty, wrap it in `Some(...)` */ + _ => arg, // If the argument is not Option, return it as is }) .collect::>()) } From 9b5b6d8e630c4028911f036da3df00c9c059516a Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 5 Nov 2024 22:15:50 +0100 Subject: [PATCH 04/13] fix: renaming and clean code --- crates/pop-contracts/src/call/metadata.rs | 51 +++++++++++++---------- crates/pop-contracts/src/call/mod.rs | 6 +-- crates/pop-contracts/src/errors.rs | 4 +- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/crates/pop-contracts/src/call/metadata.rs b/crates/pop-contracts/src/call/metadata.rs index 76737a0a..d6c3ccb5 100644 --- a/crates/pop-contracts/src/call/metadata.rs +++ b/crates/pop-contracts/src/call/metadata.rs @@ -62,8 +62,11 @@ pub fn get_messages(path: &Path) -> Result, Error> { /// # Arguments /// * `path` - Location path of the project. /// * `message` - The label of the contract message. -fn get_message(path: &Path, message: &str) -> Result { - get_messages(path)? +fn get_message

(path: P, message: &str) -> Result +where + P: AsRef, +{ + get_messages(path.as_ref())? .into_iter() .find(|msg| msg.label == message) .ok_or_else(|| Error::InvalidMessageName(message.to_string())) @@ -81,22 +84,24 @@ fn process_args(message_params: &[MessageParamSpec]) -> Vec args } -/// Generates a list of processed argument values for a specified contract message, +/// Processes a list of argument values for a specified contract message, /// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional /// /// # Arguments /// * `path` - Location path of the project. /// * `message_label` - Label of the contract message to retrieve. /// * `args` - Argument values provided by the user. -pub fn generate_message_args( - path: Option<&Path>, - message_label: &str, +pub fn process_message_args

( + path: P, + message: &str, args: Vec, -) -> Result, Error> { - let contract_path = path.unwrap_or_else(|| Path::new("./")); - let message = get_message(contract_path, message_label)?; +) -> Result, Error> +where + P: AsRef, +{ + let message = get_message(path, message)?; if args.len() != message.args.len() { - return Err(Error::WrongNumberArguments { + return Err(Error::IncorrectArguments { expected: message.args.len(), provided: args.len(), }); @@ -105,10 +110,10 @@ pub fn generate_message_args( .into_iter() .zip(&message.args) .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { - ("Option", true) => "None".to_string(), /* If the argument is Option and empty, - * replace it with `None` */ - ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not - * empty, wrap it in `Some(...)` */ + ("Option", true) => "None".to_string(), /* If the argument is Option and empty, */ + // replace it with `None` + ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not */ + // empty, wrap it in `Some(...)` _ => arg, // If the argument is not Option, return it as is }) .collect::>()) @@ -173,7 +178,7 @@ mod tests { } #[test] - fn generate_message_args_work() -> Result<()> { + fn process_message_args_work() -> Result<()> { let temp_dir = generate_smart_contract_test_environment()?; let current_dir = env::current_dir().expect("Failed to get current directory"); mock_build_process( @@ -182,27 +187,27 @@ mod tests { current_dir.join("./tests/files/testing.json"), )?; assert!(matches!( - generate_message_args(Some(&temp_dir.path().join("testing")),"wrong_flip", Vec::new()), + process_message_args(temp_dir.path().join("testing"),"wrong_flip", Vec::new()), Err(Error::InvalidMessageName(error)) if error == "wrong_flip".to_string())); assert!(matches!( - generate_message_args( - Some(&temp_dir.path().join("testing")), + process_message_args( + temp_dir.path().join("testing"), "specific_flip", Vec::new() ), - Err(Error::WrongNumberArguments {expected, provided }) if expected == 2 && provided == 0 + Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 )); assert_eq!( - generate_message_args( - Some(&temp_dir.path().join("testing")), + process_message_args( + temp_dir.path().join("testing"), "specific_flip", ["true".to_string(), "2".to_string()].to_vec() )?, ["true".to_string(), "Some(2)".to_string()] ); assert_eq!( - generate_message_args( - Some(&temp_dir.path().join("testing")), + process_message_args( + temp_dir.path().join("testing"), "specific_flip", ["true".to_string(), "".to_string()].to_vec() )?, diff --git a/crates/pop-contracts/src/call/mod.rs b/crates/pop-contracts/src/call/mod.rs index 6683541e..69d0f04e 100644 --- a/crates/pop-contracts/src/call/mod.rs +++ b/crates/pop-contracts/src/call/mod.rs @@ -67,9 +67,9 @@ pub async fn set_up_call( parse_balance(&call_opts.value)?; let contract: ::AccountId = parse_account(&call_opts.contract)?; - // Parse the argument values input by the user. - let args = metadata::generate_message_args( - call_opts.path.as_deref(), + // Process the argument values input by the user. + let args = metadata::process_message_args( + call_opts.path.unwrap_or_else(|| PathBuf::from("./")), &call_opts.message, call_opts.args, )?; diff --git a/crates/pop-contracts/src/errors.rs b/crates/pop-contracts/src/errors.rs index a22a0dcb..f0e019f1 100644 --- a/crates/pop-contracts/src/errors.rs +++ b/crates/pop-contracts/src/errors.rs @@ -56,6 +56,6 @@ pub enum Error { UnsupportedPlatform { os: &'static str }, #[error("{0}")] UploadContractError(String), - #[error("Wrong number of arguments provided. Expecting {expected}, {provided} provided")] - WrongNumberArguments { expected: usize, provided: usize }, + #[error("Incorrect number of arguments provided. Expecting {expected}, {provided} provided")] + IncorrectArguments { expected: usize, provided: usize }, } From 5b27b37c9702ad5ab01b7ebaec43ffe2811a3414 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 6 Nov 2024 09:57:13 +0100 Subject: [PATCH 05/13] chore: option params not mandatory --- crates/pop-cli/src/commands/call/contract.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index 3b22d014..204aee35 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -230,11 +230,15 @@ impl CallContractCommand { // Resolve message arguments. let mut contract_args = Vec::new(); for arg in &message.args { - contract_args.push( - cli.input(format!("Enter the value for the parameter: {}", arg.label)) - .placeholder(&format!("Type required: {}", &arg.type_name)) - .interact()?, - ); + let mut input = cli + .input(format!("Enter the value for the parameter: {}", arg.label)) + .placeholder(&format!("Type required: {}", arg.type_name)); + + // Set default input only if the parameter type is `Option` (Not mandatory) + if arg.type_name == "Option" { + input = input.default_input(""); + } + contract_args.push(input.interact()?); } self.args = contract_args; From b0c9478cf0a302d8cb0b23fc6d6d83a75365a9b7 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Wed, 6 Nov 2024 09:59:43 +0100 Subject: [PATCH 06/13] fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests --- crates/pop-cli/src/commands/call/contract.rs | 8 +- crates/pop-cli/src/commands/up/contract.rs | 2 +- .../src/{call/mod.rs => call.rs} | 3 +- crates/pop-contracts/src/errors.rs | 2 + crates/pop-contracts/src/lib.rs | 10 +- crates/pop-contracts/src/up.rs | 9 +- .../src/{call => utils}/metadata.rs | 193 +++++++++++++++++- crates/pop-contracts/src/utils/mod.rs | 1 + .../tests/files/testing.contract | 2 +- crates/pop-contracts/tests/files/testing.json | 109 +++++----- 10 files changed, 274 insertions(+), 65 deletions(-) rename crates/pop-contracts/src/{call/mod.rs => call.rs} (99%) rename crates/pop-contracts/src/{call => utils}/metadata.rs (52%) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index 204aee35..d0c3e8b1 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -760,6 +760,7 @@ mod tests { let mut cli = MockCli::new() .expect_input("Signer calling the contract:", "//Alice".into()) .expect_input("Value to transfer to the call:", "50".into()) // Only if payable + .expect_input("Enter the value for the parameter: number", "2".into()) // Args for specific_flip .expect_input("Enter the value for the parameter: new_value", "true".into()) // Args for specific_flip .expect_select::( "Select the message to call:", @@ -780,7 +781,7 @@ mod tests { "Where is your project located?", temp_dir.path().join("testing").display().to_string(), ).expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -804,8 +805,9 @@ mod tests { Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()) ); assert_eq!(call_config.message, Some("specific_flip".to_string())); - assert_eq!(call_config.args.len(), 1); + assert_eq!(call_config.args.len(), 2); assert_eq!(call_config.args[0], "true".to_string()); + assert_eq!(call_config.args[1], "2".to_string()); assert_eq!(call_config.value, "50".to_string()); assert_eq!(call_config.gas_limit, None); assert_eq!(call_config.proof_size, None); @@ -815,7 +817,7 @@ mod tests { assert!(!call_config.dry_run); assert!(call_config.dev_mode); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index d357d958..ca8112b2 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -35,7 +35,7 @@ pub struct UpContractCommand { #[clap(name = "constructor", long, default_value = "new")] constructor: String, /// The constructor arguments, encoded as strings. - #[clap(long, num_args = 0..)] + #[clap(long, num_args = 0.., value_delimiter = ',')] args: Vec, /// Transfers an initial balance to the instantiated contract. #[clap(name = "value", long, default_value = "0")] diff --git a/crates/pop-contracts/src/call/mod.rs b/crates/pop-contracts/src/call.rs similarity index 99% rename from crates/pop-contracts/src/call/mod.rs rename to crates/pop-contracts/src/call.rs index 69d0f04e..42cb7082 100644 --- a/crates/pop-contracts/src/call/mod.rs +++ b/crates/pop-contracts/src/call.rs @@ -4,6 +4,7 @@ use crate::{ errors::Error, utils::{ helpers::{get_manifest_path, parse_account, parse_balance}, + metadata, signer::create_signer, }, }; @@ -20,8 +21,6 @@ use subxt::{Config, PolkadotConfig as DefaultConfig}; use subxt_signer::sr25519::Keypair; use url::Url; -pub mod metadata; - /// Attributes for the `call` command. pub struct CallOpts { /// Path to the contract build directory. diff --git a/crates/pop-contracts/src/errors.rs b/crates/pop-contracts/src/errors.rs index f0e019f1..3d19d679 100644 --- a/crates/pop-contracts/src/errors.rs +++ b/crates/pop-contracts/src/errors.rs @@ -28,6 +28,8 @@ pub enum Error { InstallContractsNode(String), #[error("{0}")] InstantiateContractError(String), + #[error("Invalid constructor name: {0}")] + InvalidConstructorName(String), #[error("Invalid message name: {0}")] InvalidMessageName(String), #[error("Invalid name: {0}")] diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index a3ff0175..2fbb4a63 100644 --- a/crates/pop-contracts/src/lib.rs +++ b/crates/pop-contracts/src/lib.rs @@ -14,9 +14,7 @@ mod utils; pub use build::{build_smart_contract, is_supported, Verbosity}; pub use call::{ - call_smart_contract, dry_run_call, dry_run_gas_estimate_call, - metadata::{get_messages, Message}, - set_up_call, CallOpts, + call_smart_contract, dry_run_call, dry_run_gas_estimate_call, set_up_call, CallOpts, }; pub use new::{create_smart_contract, is_valid_contract_name}; pub use node::{contracts_node_generator, is_chain_alive, run_contracts_node}; @@ -27,4 +25,8 @@ pub use up::{ dry_run_gas_estimate_instantiate, dry_run_upload, instantiate_smart_contract, set_up_deployment, set_up_upload, upload_smart_contract, UpOpts, }; -pub use utils::{helpers::parse_account, signer::parse_hex_bytes}; +pub use utils::{ + helpers::parse_account, + metadata::{get_messages, Message}, + signer::parse_hex_bytes, +}; diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index 6d2f0ec0..4542211e 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -3,6 +3,7 @@ use crate::{ errors::Error, utils::{ helpers::{get_manifest_path, parse_balance}, + metadata, signer::create_signer, }, }; @@ -62,10 +63,16 @@ pub async fn set_up_deployment( let value: BalanceVariant<::Balance> = parse_balance(&up_opts.value)?; + // Process the argument values input by the user. + let args = metadata::process_constructor_args( + up_opts.path.unwrap_or_else(|| PathBuf::from("./")), + &up_opts.constructor, + up_opts.args, + )?; let instantiate_exec: InstantiateExec = InstantiateCommandBuilder::new(extrinsic_opts) .constructor(up_opts.constructor.clone()) - .args(up_opts.args.clone()) + .args(args) .value(value.denominate_balance(&token_metadata)?) .gas_limit(up_opts.gas_limit) .proof_size(up_opts.proof_size) diff --git a/crates/pop-contracts/src/call/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs similarity index 52% rename from crates/pop-contracts/src/call/metadata.rs rename to crates/pop-contracts/src/utils/metadata.rs index d6c3ccb5..89f88732 100644 --- a/crates/pop-contracts/src/call/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -14,6 +14,22 @@ pub struct Param { /// The type name of the parameter. pub type_name: String, } + +#[derive(Clone, PartialEq, Eq)] +/// Describes a contract message. +pub struct Constructor { + /// The label of the constructor. + pub label: String, + /// If the message accepts any `value` from the caller. + pub payable: bool, + /// The parameters of the deployment handler. + pub args: Vec, + /// The constructor documentation. + pub docs: String, + /// If the constructor is the default for off-chain consumers (e.g UIs). + pub default: bool, +} + #[derive(Clone, PartialEq, Eq)] /// Describes a contract message. pub struct Message { @@ -72,10 +88,50 @@ where .ok_or_else(|| Error::InvalidMessageName(message.to_string())) } -// Parse the message parameters into a vector of argument labels. -fn process_args(message_params: &[MessageParamSpec]) -> Vec { +/// Extracts a list of smart contract contructors parsing the metadata file. +/// +/// # Arguments +/// * `path` - Location path of the project. +pub fn get_constructors(path: &Path) -> Result, Error> { + let cargo_toml_path = match path.ends_with("Cargo.toml") { + true => path.to_path_buf(), + false => path.join("Cargo.toml"), + }; + let contract_artifacts = + ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; + let transcoder = contract_artifacts.contract_transcoder()?; + let mut constructors: Vec = Vec::new(); + for constructor in transcoder.metadata().spec().constructors() { + constructors.push(Constructor { + label: constructor.label().to_string(), + payable: *constructor.payable(), + args: process_args(constructor.args()), + docs: constructor.docs().join(" "), + default: *constructor.default(), + }); + } + Ok(constructors) +} + +/// Extracts the information of a smart contract constructor parsing the metadata file. +/// +/// # Arguments +/// * `path` - Location path of the project. +/// * `constructor` - The label of the constructor. +fn get_constructor

(path: P, constructor: &str) -> Result +where + P: AsRef, +{ + get_constructors(path.as_ref())? + .into_iter() + .find(|c: &Constructor| c.label == constructor) + .ok_or_else(|| Error::InvalidConstructorName(constructor.to_string())) +} + +// Parse the parameters into a vector of argument labels. +fn process_args(params: &[MessageParamSpec]) -> Vec { let mut args: Vec = Vec::new(); - for arg in message_params { + for arg in params { args.push(Param { label: arg.label().to_string(), type_name: arg.ty().display_name().to_string(), @@ -119,6 +175,41 @@ where .collect::>()) } +/// Processes a list of argument values for a specified contract constructor, +/// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional +/// +/// # Arguments +/// * `path` - Location path of the project. +/// * `constructor` - Label of the contract constructor to retrieve. +/// * `args` - Argument values provided by the user. +pub fn process_constructor_args

( + path: P, + constructor: &str, + args: Vec, +) -> Result, Error> +where + P: AsRef, +{ + let constructor = get_constructor(path, constructor)?; + if args.len() != constructor.args.len() { + return Err(Error::IncorrectArguments { + expected: constructor.args.len(), + provided: args.len(), + }); + } + Ok(args + .into_iter() + .zip(&constructor.args) + .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { + ("Option", true) => "None".to_string(), /* If the argument is Option and empty, */ + // replace it with `None` + ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not */ + // empty, wrap it in `Some(...)` + _ => arg, // If the argument is not Option, return it as is + }) + .collect::>()) +} + #[cfg(test)] mod tests { use std::env; @@ -177,6 +268,63 @@ mod tests { Ok(()) } + #[test] + fn get_constructors_work() -> Result<()> { + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + let constructor = get_constructors(&temp_dir.path().join("testing"))?; + assert_eq!(constructor.len(), 2); + assert_eq!(constructor[0].label, "new"); + assert_eq!( + constructor[0].docs, + "Constructor that initializes the `bool` value to the given `init_value`." + ); + assert_eq!(constructor[1].label, "default"); + assert_eq!( + constructor[1].docs, + "Constructor that initializes the `bool` value to `false`. Constructors can delegate to other constructors." + ); + // assert parsed arguments + assert_eq!(constructor[0].args.len(), 2); + assert_eq!(constructor[0].args[0].label, "init_value".to_string()); + assert_eq!(constructor[0].args[0].type_name, "bool".to_string()); + assert_eq!(constructor[0].args[1].label, "number".to_string()); + assert_eq!(constructor[0].args[1].type_name, "Option".to_string()); + Ok(()) + } + + #[test] + fn get_constructor_work() -> Result<()> { + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + assert!(matches!( + get_constructor(&temp_dir.path().join("testing"), "wrong_constructor"), + Err(Error::InvalidConstructorName(name)) if name == "wrong_constructor".to_string())); + let constructor = get_constructor(&temp_dir.path().join("testing"), "new")?; + assert_eq!(constructor.label, "new"); + assert_eq!( + constructor.docs, + "Constructor that initializes the `bool` value to the given `init_value`." + ); + // assert parsed arguments + assert_eq!(constructor.args.len(), 2); + assert_eq!(constructor.args[0].label, "init_value".to_string()); + assert_eq!(constructor.args[0].type_name, "bool".to_string()); + assert_eq!(constructor.args[1].label, "number".to_string()); + assert_eq!(constructor.args[1].type_name, "Option".to_string()); + Ok(()) + } + #[test] fn process_message_args_work() -> Result<()> { let temp_dir = generate_smart_contract_test_environment()?; @@ -215,4 +363,43 @@ mod tests { ); Ok(()) } + + #[test] + fn process_constructor_args_work() -> Result<()> { + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + assert!(matches!( + process_constructor_args(temp_dir.path().join("testing"),"wrong_constructor", Vec::new()), + Err(Error::InvalidConstructorName(error)) if error == "wrong_constructor".to_string())); + assert!(matches!( + process_constructor_args( + temp_dir.path().join("testing"), + "new", + Vec::new() + ), + Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 + )); + assert_eq!( + process_constructor_args( + temp_dir.path().join("testing"), + "new", + ["true".to_string(), "2".to_string()].to_vec() + )?, + ["true".to_string(), "Some(2)".to_string()] + ); + assert_eq!( + process_constructor_args( + temp_dir.path().join("testing"), + "new", + ["true".to_string(), "".to_string()].to_vec() + )?, + ["true".to_string(), "None".to_string()] + ); + Ok(()) + } } diff --git a/crates/pop-contracts/src/utils/mod.rs b/crates/pop-contracts/src/utils/mod.rs index 357c6608..ad49e2dc 100644 --- a/crates/pop-contracts/src/utils/mod.rs +++ b/crates/pop-contracts/src/utils/mod.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 pub mod helpers; +pub mod metadata; pub mod signer; diff --git a/crates/pop-contracts/tests/files/testing.contract b/crates/pop-contracts/tests/files/testing.contract index 73ff479a..7918b718 100644 --- a/crates/pop-contracts/tests/files/testing.contract +++ b/crates/pop-contracts/tests/files/testing.contract @@ -1 +1 @@ -{"source":{"hash":"0xd35166baa65bf10eb07c3d30c052d0fe752d6b98a864b61062e189be1a1ed550","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d0100000001270760027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f006000017f60017f017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f727902010210031110010101010004000506020000000002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0013066465706c6f7900140aff0d102b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0b2601017f230041106b220224002002200036020c20012002410c6a4104100a200241106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100a200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b7301037f230041106b22012400200141086a220320002802042202047f2000200241016b36020420002000280200220041016a36020020002d00000520000b3a000120032002453a000020012d0009210020012d00082102200141106a240041024101410220004101461b410020001b20021b0b12004180800441003b0100410041021011000b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031011000b920101057f230041106b220224002002428080013702082002418080043602044100200241046a22041009024020022802082206200228020c2203490d00200228020421052002410036020c2002200620036b3602082002200320056a36020420012004100b200020041009200228020c220020022802084b0d00200520032002280204200010021a200241106a24000f0b000b0d0020004180800420011003000b08002000200110100ba70501087f230041206b2200240020004180800136021441808004200041146a100102400240024020002802142202418180014f0d004104210541042106024020024104490d0020004184800436020c2000200241046b360210418380042d00002101418280042d00002104418180042d0000210302400240418080042d00002202412f470440200241ec00460d01200241e300472003413a4772200441a50147200141d1004772720d03410221010c020b200341860147200441db004772200141d90147720d02410321010c010b2003410f472004411d4772200141f70147720d012000410c6a100d220241ff01714102460d0120002802102204450d010240200028020c22032d000022010e020100020b20044105490d0120032800012106410121010b20002001360218200020023a0014410821050b200041146a220220056a2006360200200028021822044104460d01200028021c210520002d0014210620004280800137021820004180800436021441002002100920002802182207200028021c2201490d00200028021421032000200720016b220736021420032001200120036a2201200210002000280214220320074b720d0020002003360218200020013602142002100d220241ff01714102460d002000280218220141034d2001410447720d00200028021428000021000240024002404102200441026b2201200141024f1b41016b0e020100020b2005200020041b2006410047101241004100100f000b100c41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200241ff0171410047200041046a100b200028020c2200418180014f0440000b410020001011000b100c41ff01714105460d020b000b41014101100f000b2000200241ff017145101241004100100f000be90101057f230041106b2200240002400240100c41ff01714105470d0020004180800136020c418080042000410c6a1001200028020c2201418180014f0d0020014104490d012000418480043602042000200141046b360208418380042d00002101418280042d00002102418180042d000021030240418080042d0000220441ed014704402004419b0147200341ae0147722002419d0147200141de004772720d03200041046a100d220041ff01714102470d010c030b200341cb00462002419d0146712001411b4671450d02410041001010100e000b410020001010100e000b000b41014101100f000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"primitive":"u32"}}},{"id":2,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"},{"name":"number","type":1,"typeName":",>>::Type"}]}}}},{"id":3,"type":{"path":["Result"],"params":[{"name":"T","type":4},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":4}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":4,"type":{"def":{"tuple":[]}}},{"id":5,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":6,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":7,"type":{"path":["Option"],"params":[{"name":"T","type":1}],"def":{"variant":{"variants":[{"name":"None","index":0},{"name":"Some","fields":[{"type":1}],"index":1}]}}}},{"id":8,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":9,"type":{"def":{"array":{"len":32,"type":10}}}},{"id":10,"type":{"def":{"primitive":"u8"}}},{"id":11,"type":{"def":{"primitive":"u128"}}},{"id":12,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":13,"type":{"def":{"primitive":"u64"}}},{"id":14,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}},{"name":"number","layout":{"leaf":{"key":"0x00000000","ty":1}}}]}},"ty":2}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}}],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":6,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":7,"displayName":["Option"]}}],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":5,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":8,"displayName":["AccountId"]},"balance":{"type":11,"displayName":["Balance"]},"hash":{"type":12,"displayName":["Hash"]},"timestamp":{"type":13,"displayName":["Timestamp"]},"blockNumber":{"type":1,"displayName":["BlockNumber"]},"chainExtension":{"type":14,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file +{"source":{"hash":"0xea3a2d4428c3717ab229f1822014375d42a0497a0b86eb8fbb405ec6733899e9","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d0100000001270760027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f0060017f017f6000017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f7279020102100313120101010100050000040006020000000002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0015066465706c6f7900160ac910122b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0bc00101057f230041106b22022400410221044104210502402001100a220641ff01714102460d00200241086a2001100b20022d00080d000240024020022d000922030e020100020b20012802042203410449047f4101052001200341046b36020420012001280200220141046a3602002001280000210341000b2101200220033602042002200136020020022802000d0141012103200228020421040b20002003360204200020063a0000410821050b200020056a2004360200200241106a24000b3f01027f230041106b22012400200141086a2000100b20012d0009210020012d00082102200141106a240041024101410220004101461b410020001b20021b0b3c01017f200020012802042202047f2001200241016b36020420012001280200220141016a36020020012d00000520010b3a000120002002453a00000b2601017f230041106b220224002002200036020c20012002410c6a4104100d200241106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100d200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b12004180800441003b0100410041021013000b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031013000b920101057f230041106b220224002002428080013702082002418080043602044100200241046a2204100c024020022802082206200228020c2203490d00200228020421052002410036020c2002200620036b3602082002200320056a36020420012004100e20002004100c200228020c220020022802084b0d00200520032002280204200010021a200241106a24000f0b000b0d0020004180800420011003000b08002000200110120bb60501087f230041206b2200240020004180800136021441808004200041146a100102400240024020002802142201418180014f0d0041042104027f410420014104490d001a20004184800436020c2000200141046b360210418380042d00002101418280042d00002102418180042d00002103024002400240418080042d00002205412f470440200541ec00460d014104200541e300470d041a41042003413a470d041a4104200241a501470d041a41042202200141d100470d041a410221010c020b41042003418601470d031a4104200241db00470d031a41042202200141d901470d031a410321010c010b41042003410f470d021a41042002411d470d021a4104200141f701470d021a200041146a2000410c6a1009200028021822014102460d01200028021c210420002d001421020b20002001360204200020023a000041080c010b41040b20006a2004360200200028020422024104460d012000280208210520002d000021072000428080013702182000418080043602144100200041146a2203100c20002802182206200028021c2201490d00200028021421042000200620016b220636021420042001200120046a2201200310002000280214220420064b720d0020002004360218200020013602142003100a220141ff01714102460d002000280218220341034d2003410447720d00200028021428000021000240024002404102200241026b2203200341024f1b41016b0e020100020b2005200020021b20074100471014410041001011000b100f41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200141ff0171410047200041046a100e200028020c2200418180014f0440000b410020001013000b100f41ff01714105460d020b000b410141011011000b2000200141ff0171451014410041001011000bd90201067f230041206b22002400024002400240100f41ff01714105470d0020004180800136021441808004200041146a100120002802142201418180014f0d004104210220014104490d0120004184800436020c2000200141046b360210418380042d00002103418280042d00002101418180042d0000210402400240418080042d0000220541ed014704402005419b0147200441ae0147722001419d0147720d04200341de00460d010c040b200441cb00472001419d0147720d03410321012003411b470d04410221030c010b200041146a2000410c6a1009200028021822034102460d02200028021c210120002d001421020b20002003360204200020023a0000410821020c020b000b410321010b200020026a2001360200024020002802042202410347044020024102470d014100410010121010000b410141011011000b2000280208410020021b20002d000041004710121010000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"primitive":"u32"}}},{"id":2,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"},{"name":"number","type":1,"typeName":",>>::Type"}]}}}},{"id":3,"type":{"path":["Option"],"params":[{"name":"T","type":1}],"def":{"variant":{"variants":[{"name":"None","index":0},{"name":"Some","fields":[{"type":1}],"index":1}]}}}},{"id":4,"type":{"path":["Result"],"params":[{"name":"T","type":5},{"name":"E","type":6}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":5}],"index":0},{"name":"Err","fields":[{"type":6}],"index":1}]}}}},{"id":5,"type":{"def":{"tuple":[]}}},{"id":6,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":7,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":6}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":6}],"index":1}]}}}},{"id":8,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":9,"type":{"def":{"array":{"len":32,"type":10}}}},{"id":10,"type":{"def":{"primitive":"u8"}}},{"id":11,"type":{"def":{"primitive":"u128"}}},{"id":12,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":13,"type":{"def":{"primitive":"u64"}}},{"id":14,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}},{"name":"number","layout":{"leaf":{"key":"0x00000000","ty":1}}}]}},"ty":2}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":3,"displayName":["Option"]}}],"returnType":{"type":4,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[],"returnType":{"type":4,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":4,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":7,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":3,"displayName":["Option"]}}],"returnType":{"type":4,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":6,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":8,"displayName":["AccountId"]},"balance":{"type":11,"displayName":["Balance"]},"hash":{"type":12,"displayName":["Hash"]},"timestamp":{"type":13,"displayName":["Timestamp"]},"blockNumber":{"type":1,"displayName":["BlockNumber"]},"chainExtension":{"type":14,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file diff --git a/crates/pop-contracts/tests/files/testing.json b/crates/pop-contracts/tests/files/testing.json index 8f28780d..a5bad929 100644 --- a/crates/pop-contracts/tests/files/testing.json +++ b/crates/pop-contracts/tests/files/testing.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0xd35166baa65bf10eb07c3d30c052d0fe752d6b98a864b61062e189be1a1ed550", + "hash": "0xea3a2d4428c3717ab229f1822014375d42a0497a0b86eb8fbb405ec6733899e9", "language": "ink! 5.0.0", "compiler": "rustc 1.78.0", "build_info": { @@ -67,6 +67,39 @@ }, { "id": 3, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 1 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "index": 0 + }, + { + "name": "Some", + "fields": [ + { + "type": 1 + } + ], + "index": 1 + } + ] + } + } + } + }, + { + "id": 4, "type": { "path": [ "Result" @@ -74,11 +107,11 @@ "params": [ { "name": "T", - "type": 4 + "type": 5 }, { "name": "E", - "type": 5 + "type": 6 } ], "def": { @@ -88,7 +121,7 @@ "name": "Ok", "fields": [ { - "type": 4 + "type": 5 } ], "index": 0 @@ -97,7 +130,7 @@ "name": "Err", "fields": [ { - "type": 5 + "type": 6 } ], "index": 1 @@ -108,7 +141,7 @@ } }, { - "id": 4, + "id": 5, "type": { "def": { "tuple": [] @@ -116,7 +149,7 @@ } }, { - "id": 5, + "id": 6, "type": { "path": [ "ink_primitives", @@ -135,7 +168,7 @@ } }, { - "id": 6, + "id": 7, "type": { "path": [ "Result" @@ -147,7 +180,7 @@ }, { "name": "E", - "type": 5 + "type": 6 } ], "def": { @@ -166,40 +199,7 @@ "name": "Err", "fields": [ { - "type": 5 - } - ], - "index": 1 - } - ] - } - } - } - }, - { - "id": 7, - "type": { - "path": [ - "Option" - ], - "params": [ - { - "name": "T", - "type": 1 - } - ], - "def": { - "variant": { - "variants": [ - { - "name": "None", - "index": 0 - }, - { - "name": "Some", - "fields": [ - { - "type": 1 + "type": 6 } ], "index": 1 @@ -344,10 +344,19 @@ "bool" ] } + }, + { + "label": "number", + "type": { + "type": 3, + "displayName": [ + "Option" + ] + } } ], "returnType": { - "type": 3, + "type": 4, "displayName": [ "ink_primitives", "ConstructorResult" @@ -364,7 +373,7 @@ "payable": false, "args": [], "returnType": { - "type": 3, + "type": 4, "displayName": [ "ink_primitives", "ConstructorResult" @@ -386,7 +395,7 @@ "payable": false, "args": [], "returnType": { - "type": 3, + "type": 4, "displayName": [ "ink", "MessageResult" @@ -406,7 +415,7 @@ "payable": false, "args": [], "returnType": { - "type": 6, + "type": 7, "displayName": [ "ink", "MessageResult" @@ -435,7 +444,7 @@ { "label": "number", "type": { - "type": 7, + "type": 3, "displayName": [ "Option" ] @@ -443,7 +452,7 @@ } ], "returnType": { - "type": 3, + "type": 4, "displayName": [ "ink", "MessageResult" @@ -459,7 +468,7 @@ "events": [], "docs": [], "lang_error": { - "type": 5, + "type": 6, "displayName": [ "ink", "LangError" From cf09406568b8a54c67edddead4e5896cbca65778 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 6 Nov 2024 12:50:28 +0100 Subject: [PATCH 07/13] refactor: process_function_args --- crates/pop-contracts/src/call.rs | 5 +- crates/pop-contracts/src/lib.rs | 2 +- crates/pop-contracts/src/up.rs | 5 +- crates/pop-contracts/src/utils/metadata.rs | 148 ++++++++------------- 4 files changed, 60 insertions(+), 100 deletions(-) diff --git a/crates/pop-contracts/src/call.rs b/crates/pop-contracts/src/call.rs index 42cb7082..7a964c38 100644 --- a/crates/pop-contracts/src/call.rs +++ b/crates/pop-contracts/src/call.rs @@ -4,7 +4,7 @@ use crate::{ errors::Error, utils::{ helpers::{get_manifest_path, parse_account, parse_balance}, - metadata, + metadata::{process_function_args, FunctionType}, signer::create_signer, }, }; @@ -67,10 +67,11 @@ pub async fn set_up_call( let contract: ::AccountId = parse_account(&call_opts.contract)?; // Process the argument values input by the user. - let args = metadata::process_message_args( + let args = process_function_args( call_opts.path.unwrap_or_else(|| PathBuf::from("./")), &call_opts.message, call_opts.args, + FunctionType::Message, )?; let call_exec: CallExec = diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index 2fbb4a63..14228399 100644 --- a/crates/pop-contracts/src/lib.rs +++ b/crates/pop-contracts/src/lib.rs @@ -27,6 +27,6 @@ pub use up::{ }; pub use utils::{ helpers::parse_account, - metadata::{get_messages, Message}, + metadata::{get_messages, ContractFunction}, signer::parse_hex_bytes, }; diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index 4542211e..2b304123 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -3,7 +3,7 @@ use crate::{ errors::Error, utils::{ helpers::{get_manifest_path, parse_balance}, - metadata, + metadata::{process_function_args, FunctionType}, signer::create_signer, }, }; @@ -64,10 +64,11 @@ pub async fn set_up_deployment( parse_balance(&up_opts.value)?; // Process the argument values input by the user. - let args = metadata::process_constructor_args( + let args = process_function_args( up_opts.path.unwrap_or_else(|| PathBuf::from("./")), &up_opts.constructor, up_opts.args, + FunctionType::Constructor, )?; let instantiate_exec: InstantiateExec = InstantiateCommandBuilder::new(extrinsic_opts) diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index 89f88732..bf345831 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -16,8 +16,8 @@ pub struct Param { } #[derive(Clone, PartialEq, Eq)] -/// Describes a contract message. -pub struct Constructor { +/// Describes a contract function. +pub struct ContractFunction { /// The label of the constructor. pub label: String, /// If the message accepts any `value` from the caller. @@ -28,30 +28,22 @@ pub struct Constructor { pub docs: String, /// If the constructor is the default for off-chain consumers (e.g UIs). pub default: bool, + /// If the message is allowed to mutate the contract state. true for constructors. + pub mutates: bool, } #[derive(Clone, PartialEq, Eq)] -/// Describes a contract message. -pub struct Message { - /// The label of the message. - pub label: String, - /// If the message is allowed to mutate the contract state. - pub mutates: bool, - /// If the message accepts any `value` from the caller. - pub payable: bool, - /// The parameters of the deployment handler. - pub args: Vec, - /// The message documentation. - pub docs: String, - /// If the message is the default for off-chain consumers (e.g UIs). - pub default: bool, +/// Specifies the type of contract funtion, either a constructor or a message. +pub enum FunctionType { + Constructor, + Message, } /// Extracts a list of smart contract messages parsing the metadata file. /// /// # Arguments /// * `path` - Location path of the project. -pub fn get_messages(path: &Path) -> Result, Error> { +pub fn get_messages(path: &Path) -> Result, Error> { let cargo_toml_path = match path.ends_with("Cargo.toml") { true => path.to_path_buf(), false => path.join("Cargo.toml"), @@ -59,9 +51,9 @@ pub fn get_messages(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - let mut messages: Vec = Vec::new(); + let mut messages: Vec = Vec::new(); for message in transcoder.metadata().spec().messages() { - messages.push(Message { + messages.push(ContractFunction { label: message.label().to_string(), mutates: message.mutates(), payable: message.payable(), @@ -78,7 +70,7 @@ pub fn get_messages(path: &Path) -> Result, Error> { /// # Arguments /// * `path` - Location path of the project. /// * `message` - The label of the contract message. -fn get_message

(path: P, message: &str) -> Result +fn get_message

(path: P, message: &str) -> Result where P: AsRef, { @@ -92,7 +84,7 @@ where /// /// # Arguments /// * `path` - Location path of the project. -pub fn get_constructors(path: &Path) -> Result, Error> { +pub fn get_constructors(path: &Path) -> Result, Error> { let cargo_toml_path = match path.ends_with("Cargo.toml") { true => path.to_path_buf(), false => path.join("Cargo.toml"), @@ -100,14 +92,15 @@ pub fn get_constructors(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - let mut constructors: Vec = Vec::new(); + let mut constructors: Vec = Vec::new(); for constructor in transcoder.metadata().spec().constructors() { - constructors.push(Constructor { + constructors.push(ContractFunction { label: constructor.label().to_string(), payable: *constructor.payable(), args: process_args(constructor.args()), docs: constructor.docs().join(" "), default: *constructor.default(), + mutates: true, }); } Ok(constructors) @@ -118,13 +111,13 @@ pub fn get_constructors(path: &Path) -> Result, Error> { /// # Arguments /// * `path` - Location path of the project. /// * `constructor` - The label of the constructor. -fn get_constructor

(path: P, constructor: &str) -> Result +fn get_constructor

(path: P, constructor: &str) -> Result where P: AsRef, { get_constructors(path.as_ref())? .into_iter() - .find(|c: &Constructor| c.label == constructor) + .find(|c| c.label == constructor) .ok_or_else(|| Error::InvalidConstructorName(constructor.to_string())) } @@ -140,66 +133,35 @@ fn process_args(params: &[MessageParamSpec]) -> Vec { args } -/// Processes a list of argument values for a specified contract message, +/// Processes a list of argument values for a specified contract function, /// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional /// /// # Arguments /// * `path` - Location path of the project. -/// * `message_label` - Label of the contract message to retrieve. +/// * `label` - Label of the contract message to retrieve. /// * `args` - Argument values provided by the user. -pub fn process_message_args

( +pub fn process_function_args

( path: P, - message: &str, + label: &str, args: Vec, + function_type: FunctionType, ) -> Result, Error> where P: AsRef, { - let message = get_message(path, message)?; - if args.len() != message.args.len() { - return Err(Error::IncorrectArguments { - expected: message.args.len(), - provided: args.len(), - }); - } - Ok(args - .into_iter() - .zip(&message.args) - .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { - ("Option", true) => "None".to_string(), /* If the argument is Option and empty, */ - // replace it with `None` - ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not */ - // empty, wrap it in `Some(...)` - _ => arg, // If the argument is not Option, return it as is - }) - .collect::>()) -} - -/// Processes a list of argument values for a specified contract constructor, -/// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional -/// -/// # Arguments -/// * `path` - Location path of the project. -/// * `constructor` - Label of the contract constructor to retrieve. -/// * `args` - Argument values provided by the user. -pub fn process_constructor_args

( - path: P, - constructor: &str, - args: Vec, -) -> Result, Error> -where - P: AsRef, -{ - let constructor = get_constructor(path, constructor)?; - if args.len() != constructor.args.len() { + let function = match function_type { + FunctionType::Message => get_message(path, label)?, + FunctionType::Constructor => get_constructor(path, label)?, + }; + if args.len() != function.args.len() { return Err(Error::IncorrectArguments { - expected: constructor.args.len(), + expected: function.args.len(), provided: args.len(), }); } Ok(args .into_iter() - .zip(&constructor.args) + .zip(&function.args) .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { ("Option", true) => "None".to_string(), /* If the argument is Option and empty, */ // replace it with `None` @@ -326,8 +288,8 @@ mod tests { } #[test] - fn process_message_args_work() -> Result<()> { - let temp_dir = generate_smart_contract_test_environment()?; + fn process_function_args_work() -> Result<()> { + let temp_dir = new_environment("testing")?; let current_dir = env::current_dir().expect("Failed to get current directory"); mock_build_process( temp_dir.path().join("testing"), @@ -335,68 +297,64 @@ mod tests { current_dir.join("./tests/files/testing.json"), )?; assert!(matches!( - process_message_args(temp_dir.path().join("testing"),"wrong_flip", Vec::new()), + process_function_args(temp_dir.path().join("testing"),"wrong_flip", Vec::new(), FunctionType::Message), Err(Error::InvalidMessageName(error)) if error == "wrong_flip".to_string())); assert!(matches!( - process_message_args( + process_function_args( temp_dir.path().join("testing"), "specific_flip", - Vec::new() + Vec::new(), + FunctionType::Message ), Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 )); assert_eq!( - process_message_args( + process_function_args( temp_dir.path().join("testing"), "specific_flip", - ["true".to_string(), "2".to_string()].to_vec() + ["true".to_string(), "2".to_string()].to_vec(), + FunctionType::Message )?, ["true".to_string(), "Some(2)".to_string()] ); assert_eq!( - process_message_args( + process_function_args( temp_dir.path().join("testing"), "specific_flip", - ["true".to_string(), "".to_string()].to_vec() + ["true".to_string(), "".to_string()].to_vec(), + FunctionType::Message )?, ["true".to_string(), "None".to_string()] ); - Ok(()) - } - #[test] - fn process_constructor_args_work() -> Result<()> { - let temp_dir = new_environment("testing")?; - let current_dir = env::current_dir().expect("Failed to get current directory"); - mock_build_process( - temp_dir.path().join("testing"), - current_dir.join("./tests/files/testing.contract"), - current_dir.join("./tests/files/testing.json"), - )?; + // Test constructors assert!(matches!( - process_constructor_args(temp_dir.path().join("testing"),"wrong_constructor", Vec::new()), + process_function_args(temp_dir.path().join("testing"),"wrong_constructor", Vec::new(), FunctionType::Constructor), Err(Error::InvalidConstructorName(error)) if error == "wrong_constructor".to_string())); assert!(matches!( - process_constructor_args( + process_function_args( temp_dir.path().join("testing"), "new", - Vec::new() + Vec::new(), + FunctionType::Constructor ), Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 )); assert_eq!( - process_constructor_args( + process_function_args( temp_dir.path().join("testing"), "new", - ["true".to_string(), "2".to_string()].to_vec() + ["true".to_string(), "2".to_string()].to_vec(), + FunctionType::Constructor )?, ["true".to_string(), "Some(2)".to_string()] ); assert_eq!( - process_constructor_args( + process_function_args( temp_dir.path().join("testing"), "new", - ["true".to_string(), "".to_string()].to_vec() + ["true".to_string(), "".to_string()].to_vec(), + FunctionType::Constructor )?, ["true".to_string(), "None".to_string()] ); From ea538bf340521e2c102871b7ccd4de1a7b0e45e7 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 6 Nov 2024 13:00:43 +0100 Subject: [PATCH 08/13] test: update tests accordingly last changes --- crates/pop-cli/src/commands/up/contract.rs | 2 +- crates/pop-contracts/src/utils/metadata.rs | 21 +-- .../tests/files/testing.contract | 2 +- crates/pop-contracts/tests/files/testing.json | 126 ++++++++++-------- 4 files changed, 82 insertions(+), 69 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index ca8112b2..e7853144 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -321,7 +321,7 @@ mod tests { let command = UpContractCommand { path: None, constructor: "new".to_string(), - args: vec!["false".to_string()].to_vec(), + args: vec![], value: "0".to_string(), gas_limit: None, proof_size: None, diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index bf345831..cf20d878 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -252,11 +252,14 @@ mod tests { "Constructor that initializes the `bool` value to `false`. Constructors can delegate to other constructors." ); // assert parsed arguments - assert_eq!(constructor[0].args.len(), 2); + assert_eq!(constructor[0].args.len(), 1); assert_eq!(constructor[0].args[0].label, "init_value".to_string()); assert_eq!(constructor[0].args[0].type_name, "bool".to_string()); - assert_eq!(constructor[0].args[1].label, "number".to_string()); - assert_eq!(constructor[0].args[1].type_name, "Option".to_string()); + assert_eq!(constructor[1].args.len(), 2); + assert_eq!(constructor[1].args[0].label, "init_value".to_string()); + assert_eq!(constructor[1].args[0].type_name, "bool".to_string()); + assert_eq!(constructor[1].args[1].label, "number".to_string()); + assert_eq!(constructor[1].args[1].type_name, "Option".to_string()); Ok(()) } @@ -272,11 +275,11 @@ mod tests { assert!(matches!( get_constructor(&temp_dir.path().join("testing"), "wrong_constructor"), Err(Error::InvalidConstructorName(name)) if name == "wrong_constructor".to_string())); - let constructor = get_constructor(&temp_dir.path().join("testing"), "new")?; - assert_eq!(constructor.label, "new"); + let constructor = get_constructor(&temp_dir.path().join("testing"), "default")?; + assert_eq!(constructor.label, "default"); assert_eq!( constructor.docs, - "Constructor that initializes the `bool` value to the given `init_value`." + "Constructor that initializes the `bool` value to `false`. Constructors can delegate to other constructors." ); // assert parsed arguments assert_eq!(constructor.args.len(), 2); @@ -334,7 +337,7 @@ mod tests { assert!(matches!( process_function_args( temp_dir.path().join("testing"), - "new", + "default", Vec::new(), FunctionType::Constructor ), @@ -343,7 +346,7 @@ mod tests { assert_eq!( process_function_args( temp_dir.path().join("testing"), - "new", + "default", ["true".to_string(), "2".to_string()].to_vec(), FunctionType::Constructor )?, @@ -352,7 +355,7 @@ mod tests { assert_eq!( process_function_args( temp_dir.path().join("testing"), - "new", + "default", ["true".to_string(), "".to_string()].to_vec(), FunctionType::Constructor )?, diff --git a/crates/pop-contracts/tests/files/testing.contract b/crates/pop-contracts/tests/files/testing.contract index 7918b718..fbd8e6fb 100644 --- a/crates/pop-contracts/tests/files/testing.contract +++ b/crates/pop-contracts/tests/files/testing.contract @@ -1 +1 @@ -{"source":{"hash":"0xea3a2d4428c3717ab229f1822014375d42a0497a0b86eb8fbb405ec6733899e9","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d0100000001270760027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f0060017f017f6000017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f7279020102100313120101010100050000040006020000000002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0015066465706c6f7900160ac910122b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0bc00101057f230041106b22022400410221044104210502402001100a220641ff01714102460d00200241086a2001100b20022d00080d000240024020022d000922030e020100020b20012802042203410449047f4101052001200341046b36020420012001280200220141046a3602002001280000210341000b2101200220033602042002200136020020022802000d0141012103200228020421040b20002003360204200020063a0000410821050b200020056a2004360200200241106a24000b3f01027f230041106b22012400200141086a2000100b20012d0009210020012d00082102200141106a240041024101410220004101461b410020001b20021b0b3c01017f200020012802042202047f2001200241016b36020420012001280200220141016a36020020012d00000520010b3a000120002002453a00000b2601017f230041106b220224002002200036020c20012002410c6a4104100d200241106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100d200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b12004180800441003b0100410041021013000b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031013000b920101057f230041106b220224002002428080013702082002418080043602044100200241046a2204100c024020022802082206200228020c2203490d00200228020421052002410036020c2002200620036b3602082002200320056a36020420012004100e20002004100c200228020c220020022802084b0d00200520032002280204200010021a200241106a24000f0b000b0d0020004180800420011003000b08002000200110120bb60501087f230041206b2200240020004180800136021441808004200041146a100102400240024020002802142201418180014f0d0041042104027f410420014104490d001a20004184800436020c2000200141046b360210418380042d00002101418280042d00002102418180042d00002103024002400240418080042d00002205412f470440200541ec00460d014104200541e300470d041a41042003413a470d041a4104200241a501470d041a41042202200141d100470d041a410221010c020b41042003418601470d031a4104200241db00470d031a41042202200141d901470d031a410321010c010b41042003410f470d021a41042002411d470d021a4104200141f701470d021a200041146a2000410c6a1009200028021822014102460d01200028021c210420002d001421020b20002001360204200020023a000041080c010b41040b20006a2004360200200028020422024104460d012000280208210520002d000021072000428080013702182000418080043602144100200041146a2203100c20002802182206200028021c2201490d00200028021421042000200620016b220636021420042001200120046a2201200310002000280214220420064b720d0020002004360218200020013602142003100a220141ff01714102460d002000280218220341034d2003410447720d00200028021428000021000240024002404102200241026b2203200341024f1b41016b0e020100020b2005200020021b20074100471014410041001011000b100f41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200141ff0171410047200041046a100e200028020c2200418180014f0440000b410020001013000b100f41ff01714105460d020b000b410141011011000b2000200141ff0171451014410041001011000bd90201067f230041206b22002400024002400240100f41ff01714105470d0020004180800136021441808004200041146a100120002802142201418180014f0d004104210220014104490d0120004184800436020c2000200141046b360210418380042d00002103418280042d00002101418180042d0000210402400240418080042d0000220541ed014704402005419b0147200441ae0147722001419d0147720d04200341de00460d010c040b200441cb00472001419d0147720d03410321012003411b470d04410221030c010b200041146a2000410c6a1009200028021822034102460d02200028021c210120002d001421020b20002003360204200020023a0000410821020c020b000b410321010b200020026a2001360200024020002802042202410347044020024102470d014100410010121010000b410141011011000b2000280208410020021b20002d000041004710121010000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"primitive":"u32"}}},{"id":2,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"},{"name":"number","type":1,"typeName":",>>::Type"}]}}}},{"id":3,"type":{"path":["Option"],"params":[{"name":"T","type":1}],"def":{"variant":{"variants":[{"name":"None","index":0},{"name":"Some","fields":[{"type":1}],"index":1}]}}}},{"id":4,"type":{"path":["Result"],"params":[{"name":"T","type":5},{"name":"E","type":6}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":5}],"index":0},{"name":"Err","fields":[{"type":6}],"index":1}]}}}},{"id":5,"type":{"def":{"tuple":[]}}},{"id":6,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":7,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":6}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":6}],"index":1}]}}}},{"id":8,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":9,"type":{"def":{"array":{"len":32,"type":10}}}},{"id":10,"type":{"def":{"primitive":"u8"}}},{"id":11,"type":{"def":{"primitive":"u128"}}},{"id":12,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":13,"type":{"def":{"primitive":"u64"}}},{"id":14,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}},{"name":"number","layout":{"leaf":{"key":"0x00000000","ty":1}}}]}},"ty":2}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":3,"displayName":["Option"]}}],"returnType":{"type":4,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[],"returnType":{"type":4,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":4,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":7,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":3,"displayName":["Option"]}}],"returnType":{"type":4,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":6,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":8,"displayName":["AccountId"]},"balance":{"type":11,"displayName":["Balance"]},"hash":{"type":12,"displayName":["Hash"]},"timestamp":{"type":13,"displayName":["Timestamp"]},"blockNumber":{"type":1,"displayName":["BlockNumber"]},"chainExtension":{"type":14,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file +{"source":{"hash":"0xf4f0cafd08d8e362141b3c64e3c651ad6a38225dbf0b66f691c15bb0ea00eac3","language":"ink! 5.0.0","compiler":"rustc 1.78.0","wasm":"0x0061736d0100000001270760027f7f0060037f7f7f017f60000060047f7f7f7f017f60037f7f7f0060017f017f6000017f027406057365616c310b6765745f73746f726167650003057365616c3005696e7075740000057365616c320b7365745f73746f726167650003057365616c300b7365616c5f72657475726e0004057365616c301176616c75655f7472616e73666572726564000003656e76066d656d6f7279020102100313120101010100050000040006000200000002020616037f01418080040b7f00418080050b7f00418080050b0711020463616c6c0015066465706c6f7900160ad910122b01017f037f2002200346047f200005200020036a200120036a2d00003a0000200341016a21030c010b0b0b6f01017f0240200020014d04402000210303402002450d02200320012d00003a0000200341016a2103200141016a2101200241016b21020c000b000b200041016b2103200141016b210103402002450d01200220036a200120026a2d00003a0000200241016b21020c000b000b20000b2501017f037f2002200346047f200005200020036a20013a0000200341016a21030c010b0b0b3f01027f0340200245044041000f0b200241016b210220012d0000210320002d00002104200141016a2101200041016a210020032004460d000b200420036b0bc00101057f230041106b22022400410221044104210502402001100a220641ff01714102460d00200241086a2001100b20022d00080d000240024020022d000922030e020100020b20012802042203410449047f4101052001200341046b36020420012001280200220141046a3602002001280000210341000b2101200220033602042002200136020020022802000d0141012103200228020421040b20002003360204200020063a0000410821050b200020056a2004360200200241106a24000b3f01027f230041106b22012400200141086a2000100b20012d0009210020012d00082102200141106a240041024101410220004101461b410020001b20021b0b3c01017f200020012802042202047f2001200241016b36020420012001280200220141016a36020020012d00000520010b3a000120002002453a00000b2601017f230041106b220224002002200036020c20012002410c6a4104100d200241106a24000b4801027f024002402000280208220320026a22042003490d00200420002802044b0d00200420036b2002470d01200028020020036a2001200210051a200020043602080f0b000b000b2601017f230041106b22022400200220003a000f20012002410f6a4101100d200241106a24000b6102027f027e230041206b22002400200041106a22014200370300200042003703082000411036021c200041086a2000411c6a1004200028021c41114f0440000b2001290300210220002903082103200041206a2400410541042002200384501b0b3c01027f027f200145044041808004210141010c010b410121024180800441013a000041818004210141020b2103200120023a0000200020031013000b12004180800441003b0100410041021013000b920101057f230041106b220224002002428080013702082002418080043602044100200241046a2204100c024020022802082206200228020c2203490d00200228020421052002410036020c2002200620036b3602082002200320056a36020420012004100e20002004100c200228020c220020022802084b0d00200520032002280204200010021a200241106a24000f0b000b0d0020004180800420011003000b08002000200110120bb60501087f230041206b2200240020004180800136021441808004200041146a100102400240024020002802142201418180014f0d0041042104027f410420014104490d001a20004184800436020c2000200141046b360210418380042d00002101418280042d00002102418180042d00002103024002400240418080042d00002205412f470440200541ec00460d014104200541e300470d041a41042003413a470d041a4104200241a501470d041a41042202200141d100470d041a410221010c020b41042003418601470d031a4104200241db00470d031a41042202200141d901470d031a410321010c010b41042003410f470d021a41042002411d470d021a4104200141f701470d021a200041146a2000410c6a1009200028021822014102460d01200028021c210420002d001421020b20002001360204200020023a000041080c010b41040b20006a2004360200200028020422024104460d012000280208210520002d000021072000428080013702182000418080043602144100200041146a2203100c20002802182206200028021c2201490d00200028021421042000200620016b220636021420042001200120046a2201200310002000280214220420064b720d0020002004360218200020013602142003100a220141ff01714102460d002000280218220341034d2003410447720d00200028021428000021000240024002404102200241026b2203200341024f1b41016b0e020100020b2005200020021b20074100471014410041001010000b100f41ff01714105470d01230041106b220024002000418080043602044180800441003a00002000428080818010370208200141ff0171410047200041046a100e200028020c2200418180014f0440000b410020001013000b100f41ff01714105460d020b000b410141011010000b2000200141ff0171451014410041001010000be90201067f230041206b22002400024002400240100f41ff01714105470d0020004180800136021441808004200041146a100120002802142201418180014f0d004104210220014104490d0120004184800436020c2000200141046b360210418380042d00002101418280042d00002103418180042d00002104027f418080042d0000220541ed014704402005419b0147200441ae0147722003419d0147200141de004772720d0341022103410322012000410c6a100a220441ff01714102470d011a0c040b200441cb00472003419d0147722001411b47720d02200041146a2000410c6a1009200028021822034102460d0220002d00142104200028021c0b210120002003360204200020043a0000410821020c020b000b410321010b200020026a2001360200024020002802042201410347044020002d0000210220014102460d012000280208410020011b200241004710121011000b410141011010000b4100200210121011000b","build_info":{"rust_toolchain":"stable-aarch64-apple-darwin","cargo_contract_version":"5.0.0-alpha","build_mode":"Release","wasm_opt_settings":{"optimization_passes":"Z","keep_debug_symbols":false}}},"contract":{"name":"testing","version":"0.1.0","authors":["[your_name] <[your_email]>"]},"image":null,"version":5,"types":[{"id":0,"type":{"def":{"primitive":"bool"}}},{"id":1,"type":{"def":{"primitive":"u32"}}},{"id":2,"type":{"path":["testing","testing","Testing"],"def":{"composite":{"fields":[{"name":"value","type":0,"typeName":",>>::Type"},{"name":"number","type":1,"typeName":",>>::Type"}]}}}},{"id":3,"type":{"path":["Result"],"params":[{"name":"T","type":4},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":4}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":4,"type":{"def":{"tuple":[]}}},{"id":5,"type":{"path":["ink_primitives","LangError"],"def":{"variant":{"variants":[{"name":"CouldNotReadInput","index":1}]}}}},{"id":6,"type":{"path":["Option"],"params":[{"name":"T","type":1}],"def":{"variant":{"variants":[{"name":"None","index":0},{"name":"Some","fields":[{"type":1}],"index":1}]}}}},{"id":7,"type":{"path":["Result"],"params":[{"name":"T","type":0},{"name":"E","type":5}],"def":{"variant":{"variants":[{"name":"Ok","fields":[{"type":0}],"index":0},{"name":"Err","fields":[{"type":5}],"index":1}]}}}},{"id":8,"type":{"path":["ink_primitives","types","AccountId"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":9,"type":{"def":{"array":{"len":32,"type":10}}}},{"id":10,"type":{"def":{"primitive":"u8"}}},{"id":11,"type":{"def":{"primitive":"u128"}}},{"id":12,"type":{"path":["ink_primitives","types","Hash"],"def":{"composite":{"fields":[{"type":9,"typeName":"[u8; 32]"}]}}}},{"id":13,"type":{"def":{"primitive":"u64"}}},{"id":14,"type":{"path":["ink_env","types","NoChainExtension"],"def":{"variant":{}}}}],"storage":{"root":{"root_key":"0x00000000","layout":{"struct":{"name":"Testing","fields":[{"name":"value","layout":{"leaf":{"key":"0x00000000","ty":0}}},{"name":"number","layout":{"leaf":{"key":"0x00000000","ty":1}}}]}},"ty":2}},"spec":{"constructors":[{"label":"new","selector":"0x9bae9d5e","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}}],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to the given `init_value`."],"default":false},{"label":"default","selector":"0xed4b9d1b","payable":false,"args":[{"label":"init_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":6,"displayName":["Option"]}}],"returnType":{"type":3,"displayName":["ink_primitives","ConstructorResult"]},"docs":["Constructor that initializes the `bool` value to `false`.","","Constructors can delegate to other constructors."],"default":false}],"messages":[{"label":"flip","selector":"0x633aa551","mutates":true,"payable":false,"args":[],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message that can be called on instantiated contracts."," This one flips the value of the stored `bool` from `true`"," to `false` and vice versa."],"default":false},{"label":"get","selector":"0x2f865bd9","mutates":false,"payable":false,"args":[],"returnType":{"type":7,"displayName":["ink","MessageResult"]},"docs":[" Simply returns the current value of our `bool`."],"default":false},{"label":"specific_flip","selector":"0x6c0f1df7","mutates":true,"payable":true,"args":[{"label":"new_value","type":{"type":0,"displayName":["bool"]}},{"label":"number","type":{"type":6,"displayName":["Option"]}}],"returnType":{"type":3,"displayName":["ink","MessageResult"]},"docs":[" A message for testing, flips the value of the stored `bool` with `new_value`"," and is payable"],"default":false}],"events":[],"docs":[],"lang_error":{"type":5,"displayName":["ink","LangError"]},"environment":{"accountId":{"type":8,"displayName":["AccountId"]},"balance":{"type":11,"displayName":["Balance"]},"hash":{"type":12,"displayName":["Hash"]},"timestamp":{"type":13,"displayName":["Timestamp"]},"blockNumber":{"type":1,"displayName":["BlockNumber"]},"chainExtension":{"type":14,"displayName":["ChainExtension"]},"maxEventTopics":4,"staticBufferSize":16384}}} \ No newline at end of file diff --git a/crates/pop-contracts/tests/files/testing.json b/crates/pop-contracts/tests/files/testing.json index a5bad929..61921986 100644 --- a/crates/pop-contracts/tests/files/testing.json +++ b/crates/pop-contracts/tests/files/testing.json @@ -1,6 +1,6 @@ { "source": { - "hash": "0xea3a2d4428c3717ab229f1822014375d42a0497a0b86eb8fbb405ec6733899e9", + "hash": "0xf4f0cafd08d8e362141b3c64e3c651ad6a38225dbf0b66f691c15bb0ea00eac3", "language": "ink! 5.0.0", "compiler": "rustc 1.78.0", "build_info": { @@ -67,39 +67,6 @@ }, { "id": 3, - "type": { - "path": [ - "Option" - ], - "params": [ - { - "name": "T", - "type": 1 - } - ], - "def": { - "variant": { - "variants": [ - { - "name": "None", - "index": 0 - }, - { - "name": "Some", - "fields": [ - { - "type": 1 - } - ], - "index": 1 - } - ] - } - } - } - }, - { - "id": 4, "type": { "path": [ "Result" @@ -107,11 +74,11 @@ "params": [ { "name": "T", - "type": 5 + "type": 4 }, { "name": "E", - "type": 6 + "type": 5 } ], "def": { @@ -121,7 +88,7 @@ "name": "Ok", "fields": [ { - "type": 5 + "type": 4 } ], "index": 0 @@ -130,7 +97,7 @@ "name": "Err", "fields": [ { - "type": 6 + "type": 5 } ], "index": 1 @@ -141,7 +108,7 @@ } }, { - "id": 5, + "id": 4, "type": { "def": { "tuple": [] @@ -149,7 +116,7 @@ } }, { - "id": 6, + "id": 5, "type": { "path": [ "ink_primitives", @@ -167,6 +134,39 @@ } } }, + { + "id": 6, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 1 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "index": 0 + }, + { + "name": "Some", + "fields": [ + { + "type": 1 + } + ], + "index": 1 + } + ] + } + } + } + }, { "id": 7, "type": { @@ -180,7 +180,7 @@ }, { "name": "E", - "type": 6 + "type": 5 } ], "def": { @@ -199,7 +199,7 @@ "name": "Err", "fields": [ { - "type": 6 + "type": 5 } ], "index": 1 @@ -344,19 +344,10 @@ "bool" ] } - }, - { - "label": "number", - "type": { - "type": 3, - "displayName": [ - "Option" - ] - } } ], "returnType": { - "type": 4, + "type": 3, "displayName": [ "ink_primitives", "ConstructorResult" @@ -371,9 +362,28 @@ "label": "default", "selector": "0xed4b9d1b", "payable": false, - "args": [], + "args": [ + { + "label": "init_value", + "type": { + "type": 0, + "displayName": [ + "bool" + ] + } + }, + { + "label": "number", + "type": { + "type": 6, + "displayName": [ + "Option" + ] + } + } + ], "returnType": { - "type": 4, + "type": 3, "displayName": [ "ink_primitives", "ConstructorResult" @@ -395,7 +405,7 @@ "payable": false, "args": [], "returnType": { - "type": 4, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -444,7 +454,7 @@ { "label": "number", "type": { - "type": 3, + "type": 6, "displayName": [ "Option" ] @@ -452,7 +462,7 @@ } ], "returnType": { - "type": 4, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -468,7 +478,7 @@ "events": [], "docs": [], "lang_error": { - "type": 6, + "type": 5, "displayName": [ "ink", "LangError" From 314fa45af21cefab0e3d689b9438998fdc68a5fe Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 6 Nov 2024 14:53:04 +0100 Subject: [PATCH 09/13] fix: issue with delimiter --- crates/pop-cli/src/commands/call/contract.rs | 14 ++++++-------- crates/pop-cli/src/commands/up/contract.rs | 2 +- crates/pop-contracts/src/utils/metadata.rs | 7 ++++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index d0c3e8b1..9a5a61c3 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -30,7 +30,7 @@ pub struct CallContractCommand { #[clap(long, short)] message: Option, /// The constructor arguments, encoded as strings. - #[clap(long, num_args = 0.., value_delimiter = ',')] + #[clap(long, num_args = 0..,)] args: Vec, /// The value to be transferred as part of the call. #[clap(name = "value", short = 'v', long, default_value = DEFAULT_PAYABLE_VALUE)] @@ -482,7 +482,7 @@ mod tests { .expect_warning("Your call has not been executed.") .expect_info("Gas limit: Weight { ref_time: 100, proof_size: 10 }"); - let mut call_config = CallContractCommand { + let call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("flip".to_string()), @@ -549,7 +549,7 @@ mod tests { .expect_outro("Contract calling complete."); // Contract deployed on Pop Network testnet, test get - let mut call_config = CallContractCommand { + let call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("get".to_string()), @@ -760,7 +760,6 @@ mod tests { let mut cli = MockCli::new() .expect_input("Signer calling the contract:", "//Alice".into()) .expect_input("Value to transfer to the call:", "50".into()) // Only if payable - .expect_input("Enter the value for the parameter: number", "2".into()) // Args for specific_flip .expect_input("Enter the value for the parameter: new_value", "true".into()) // Args for specific_flip .expect_select::( "Select the message to call:", @@ -781,7 +780,7 @@ mod tests { "Where is your project located?", temp_dir.path().join("testing").display().to_string(), ).expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -805,9 +804,8 @@ mod tests { Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()) ); assert_eq!(call_config.message, Some("specific_flip".to_string())); - assert_eq!(call_config.args.len(), 2); + assert_eq!(call_config.args.len(), 1); assert_eq!(call_config.args[0], "true".to_string()); - assert_eq!(call_config.args[1], "2".to_string()); assert_eq!(call_config.value, "50".to_string()); assert_eq!(call_config.gas_limit, None); assert_eq!(call_config.proof_size, None); @@ -817,7 +815,7 @@ mod tests { assert!(!call_config.dry_run); assert!(call_config.dev_mode); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index e7853144..42166a69 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -35,7 +35,7 @@ pub struct UpContractCommand { #[clap(name = "constructor", long, default_value = "new")] constructor: String, /// The constructor arguments, encoded as strings. - #[clap(long, num_args = 0.., value_delimiter = ',')] + #[clap(long, num_args = 0..,)] args: Vec, /// Transfers an initial balance to the instantiated contract. #[clap(name = "value", long, default_value = "0")] diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index cf20d878..4b84fa25 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -149,17 +149,18 @@ pub fn process_function_args

( where P: AsRef, { + let parsed_args = args.into_iter().map(|arg| arg.replace(",", "")).collect::>(); let function = match function_type { FunctionType::Message => get_message(path, label)?, FunctionType::Constructor => get_constructor(path, label)?, }; - if args.len() != function.args.len() { + if parsed_args.len() != function.args.len() { return Err(Error::IncorrectArguments { expected: function.args.len(), - provided: args.len(), + provided: parsed_args.len(), }); } - Ok(args + Ok(parsed_args .into_iter() .zip(&function.args) .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { From 069fc378bb4e316eb75a23733e06c5dadb38c6ae Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 6 Nov 2024 23:37:44 +0100 Subject: [PATCH 10/13] test: fix unit test --- crates/pop-contracts/src/utils/metadata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index 4b84fa25..eb9c7b6d 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -209,7 +209,7 @@ mod tests { #[test] fn get_message_work() -> Result<()> { - let temp_dir = generate_smart_contract_test_environment()?; + let temp_dir = new_environment("testing")?; let current_dir = env::current_dir().expect("Failed to get current directory"); mock_build_process( temp_dir.path().join("testing"), From 2defc640ebce78f7817e9e9c6153195302f0af81 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 7 Nov 2024 15:22:36 +0100 Subject: [PATCH 11/13] refactor: renaming and fix comments --- crates/pop-cli/src/commands/call/contract.rs | 2 +- crates/pop-contracts/src/utils/metadata.rs | 44 +++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index 9a5a61c3..a0f28dc4 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -176,7 +176,7 @@ impl CallContractCommand { }; // Parse the contract metadata provided. If there is an error, do not prompt for more. - let messages = match get_messages(&contract_path) { + let messages = match get_messages(contract_path) { Ok(messages) => messages, Err(e) => { return Err(anyhow!(format!( diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index eb9c7b6d..a16e7876 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -6,8 +6,8 @@ use contract_transcode::ink_metadata::MessageParamSpec; use scale_info::form::PortableForm; use std::path::Path; +/// Describes a parameter. #[derive(Clone, PartialEq, Eq)] -/// Describes a contract message. pub struct Param { /// The label of the parameter. pub label: String, @@ -15,25 +15,25 @@ pub struct Param { pub type_name: String, } -#[derive(Clone, PartialEq, Eq)] /// Describes a contract function. +#[derive(Clone, PartialEq, Eq)] pub struct ContractFunction { - /// The label of the constructor. + /// The label of the function. pub label: String, - /// If the message accepts any `value` from the caller. + /// If the function accepts any `value` from the caller. pub payable: bool, /// The parameters of the deployment handler. pub args: Vec, - /// The constructor documentation. + /// The function documentation. pub docs: String, - /// If the constructor is the default for off-chain consumers (e.g UIs). + /// If the message/constructor is the default for off-chain consumers (e.g UIs). pub default: bool, /// If the message is allowed to mutate the contract state. true for constructors. pub mutates: bool, } -#[derive(Clone, PartialEq, Eq)] /// Specifies the type of contract funtion, either a constructor or a message. +#[derive(Clone, PartialEq, Eq)] pub enum FunctionType { Constructor, Message, @@ -51,18 +51,20 @@ pub fn get_messages(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - let mut messages: Vec = Vec::new(); - for message in transcoder.metadata().spec().messages() { - messages.push(ContractFunction { + Ok(transcoder + .metadata() + .spec() + .messages() + .iter() + .map(|message| ContractFunction { label: message.label().to_string(), mutates: message.mutates(), payable: message.payable(), args: process_args(message.args()), docs: message.docs().join(" "), default: *message.default(), - }); - } - Ok(messages) + }) + .collect()) } /// Extracts the information of a smart contract message parsing the metadata file. @@ -92,18 +94,20 @@ pub fn get_constructors(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - let mut constructors: Vec = Vec::new(); - for constructor in transcoder.metadata().spec().constructors() { - constructors.push(ContractFunction { + Ok(transcoder + .metadata() + .spec() + .constructors() + .iter() + .map(|constructor| ContractFunction { label: constructor.label().to_string(), payable: *constructor.payable(), args: process_args(constructor.args()), docs: constructor.docs().join(" "), default: *constructor.default(), mutates: true, - }); - } - Ok(constructors) + }) + .collect()) } /// Extracts the information of a smart contract constructor parsing the metadata file. @@ -149,7 +153,7 @@ pub fn process_function_args

( where P: AsRef, { - let parsed_args = args.into_iter().map(|arg| arg.replace(",", "")).collect::>(); + let parsed_args = args.into_iter().map(|arg| arg.replace(",", "")).collect::>(); let function = match function_type { FunctionType::Message => get_message(path, label)?, FunctionType::Constructor => get_constructor(path, label)?, From c69ca1679498b4ae883150d58b703bef9db59ddf Mon Sep 17 00:00:00 2001 From: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:35:46 +0000 Subject: [PATCH 12/13] refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. --- crates/pop-cli/src/commands/call/contract.rs | 21 +- crates/pop-contracts/src/utils/metadata.rs | 201 ++++++++++++++++--- 2 files changed, 186 insertions(+), 36 deletions(-) diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index a0f28dc4..8db1cc63 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -96,7 +96,8 @@ impl CallContractCommand { full_message.push_str(&format!(" --message {}", message)); } if !self.args.is_empty() { - full_message.push_str(&format!(" --args {}", self.args.join(","))); + let args: Vec<_> = self.args.iter().map(|a| format!("\"{a}\"")).collect(); + full_message.push_str(&format!(" --args {}", args.join(", "))); } if self.value != DEFAULT_PAYABLE_VALUE { full_message.push_str(&format!(" --value {}", self.value)); @@ -235,7 +236,7 @@ impl CallContractCommand { .placeholder(&format!("Type required: {}", arg.type_name)); // Set default input only if the parameter type is `Option` (Not mandatory) - if arg.type_name == "Option" { + if arg.type_name.starts_with("Option<") { input = input.default_input(""); } contract_args.push(input.interact()?); @@ -482,7 +483,7 @@ mod tests { .expect_warning("Your call has not been executed.") .expect_info("Gas limit: Weight { ref_time: 100, proof_size: 10 }"); - let call_config = CallContractCommand { + let mut call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("flip".to_string()), @@ -549,7 +550,7 @@ mod tests { .expect_outro("Contract calling complete."); // Contract deployed on Pop Network testnet, test get - let call_config = CallContractCommand { + let mut call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("get".to_string()), @@ -696,7 +697,7 @@ mod tests { "Where is your project located?", temp_dir.path().join("testing").display().to_string(), ).expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -731,7 +732,7 @@ mod tests { assert!(call_config.execute); assert!(!call_config.dry_run); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true,2 --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -760,6 +761,7 @@ mod tests { let mut cli = MockCli::new() .expect_input("Signer calling the contract:", "//Alice".into()) .expect_input("Value to transfer to the call:", "50".into()) // Only if payable + .expect_input("Enter the value for the parameter: number", "2".into()) // Args for specific_flip .expect_input("Enter the value for the parameter: new_value", "true".into()) // Args for specific_flip .expect_select::( "Select the message to call:", @@ -780,7 +782,7 @@ mod tests { "Where is your project located?", temp_dir.path().join("testing").display().to_string(), ).expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -804,8 +806,9 @@ mod tests { Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()) ); assert_eq!(call_config.message, Some("specific_flip".to_string())); - assert_eq!(call_config.args.len(), 1); + assert_eq!(call_config.args.len(), 2); assert_eq!(call_config.args[0], "true".to_string()); + assert_eq!(call_config.args[1], "2".to_string()); assert_eq!(call_config.value, "50".to_string()); assert_eq!(call_config.gas_limit, None); assert_eq!(call_config.proof_size, None); @@ -815,7 +818,7 @@ mod tests { assert!(!call_config.dry_run); assert!(call_config.dev_mode); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args true --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", temp_dir.path().join("testing").display().to_string(), )); diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index a16e7876..f0dd2b8e 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -3,11 +3,11 @@ use crate::errors::Error; use contract_extrinsics::ContractArtifacts; use contract_transcode::ink_metadata::MessageParamSpec; -use scale_info::form::PortableForm; +use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef, TypeDefPrimitive}; use std::path::Path; /// Describes a parameter. -#[derive(Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Param { /// The label of the parameter. pub label: String, @@ -51,8 +51,8 @@ pub fn get_messages(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - Ok(transcoder - .metadata() + let metadata = transcoder.metadata(); + Ok(metadata .spec() .messages() .iter() @@ -60,7 +60,7 @@ pub fn get_messages(path: &Path) -> Result, Error> { label: message.label().to_string(), mutates: message.mutates(), payable: message.payable(), - args: process_args(message.args()), + args: process_args(message.args(), metadata.registry()), docs: message.docs().join(" "), default: *message.default(), }) @@ -94,15 +94,15 @@ pub fn get_constructors(path: &Path) -> Result, Error> { let contract_artifacts = ContractArtifacts::from_manifest_or_file(Some(&cargo_toml_path), None)?; let transcoder = contract_artifacts.contract_transcoder()?; - Ok(transcoder - .metadata() + let metadata = transcoder.metadata(); + Ok(metadata .spec() .constructors() .iter() .map(|constructor| ContractFunction { label: constructor.label().to_string(), payable: *constructor.payable(), - args: process_args(constructor.args()), + args: process_args(constructor.args(), metadata.registry()), docs: constructor.docs().join(" "), default: *constructor.default(), mutates: true, @@ -126,17 +126,164 @@ where } // Parse the parameters into a vector of argument labels. -fn process_args(params: &[MessageParamSpec]) -> Vec { +fn process_args( + params: &[MessageParamSpec], + registry: &PortableRegistry, +) -> Vec { let mut args: Vec = Vec::new(); for arg in params { - args.push(Param { - label: arg.label().to_string(), - type_name: arg.ty().display_name().to_string(), - }); + // Resolve type from registry to provide full type representation. + let type_name = + format_type(registry.resolve(arg.ty().ty().id).expect("type not found"), registry); + args.push(Param { label: arg.label().to_string(), type_name }); } args } +// Formats a specified type, using the registry to output its full type representation. +fn format_type(ty: &Type, registry: &PortableRegistry) -> String { + let mut name = ty + .path + .segments + .last() + .map(|s| s.to_owned()) + .unwrap_or_else(|| ty.path.to_string()); + + if !ty.type_params.is_empty() { + let params: Vec<_> = ty + .type_params + .iter() + .filter_map(|p| registry.resolve(p.ty.unwrap().id)) + .map(|t| format_type(t, registry)) + .collect(); + name = format!("{name}<{}>", params.join(",")); + } + + name = format!( + "{name}{}", + match &ty.type_def { + TypeDef::Composite(composite) => { + if composite.fields.is_empty() { + return "".to_string(); + } + + let mut named = false; + let fields: Vec<_> = composite + .fields + .iter() + .filter_map(|f| match f.name.as_ref() { + None => registry.resolve(f.ty.id).map(|t| format_type(t, registry)), + Some(field) => { + named = true; + f.type_name.as_ref().map(|t| format!("{field}: {t}")) + }, + }) + .collect(); + match named { + true => format!(" {{ {} }}", fields.join(", ")), + false => format!(" ({})", fields.join(", ")), + } + }, + TypeDef::Variant(variant) => { + let variants: Vec<_> = variant + .variants + .iter() + .map(|v| { + if v.fields.is_empty() { + return v.name.clone(); + } + + let name = v.name.as_str(); + let mut named = false; + let fields: Vec<_> = v + .fields + .iter() + .filter_map(|f| match f.name.as_ref() { + None => registry.resolve(f.ty.id).map(|t| format_type(t, registry)), + Some(field) => { + named = true; + f.type_name.as_ref().map(|t| format!("{field}: {t}")) + }, + }) + .collect(); + format!( + "{name}{}", + match named { + true => format!("{{ {} }}", fields.join(", ")), + false => format!("({})", fields.join(", ")), + } + ) + }) + .collect(); + format!(": {}", variants.join(", ")) + }, + TypeDef::Sequence(sequence) => { + format!( + "[{}]", + format_type( + registry.resolve(sequence.type_param.id).expect("sequence type not found"), + registry + ) + ) + }, + TypeDef::Array(array) => { + format!( + "[{};{}]", + format_type( + registry.resolve(array.type_param.id).expect("array type not found"), + registry + ), + array.len + ) + }, + TypeDef::Tuple(tuple) => { + let fields: Vec<_> = tuple + .fields + .iter() + .filter_map(|p| registry.resolve(p.id)) + .map(|t| format_type(t, registry)) + .collect(); + format!("({})", fields.join(",")) + }, + TypeDef::Primitive(primitive) => { + use TypeDefPrimitive::*; + match primitive { + Bool => "bool", + Char => "char", + Str => "str", + U8 => "u8", + U16 => "u16", + U32 => "u32", + U64 => "u64", + U128 => "u128", + U256 => "u256", + I8 => "i8", + I16 => "i16", + I32 => "i32", + I64 => "i64", + I128 => "i128", + I256 => "i256", + } + .to_string() + }, + TypeDef::Compact(compact) => { + format!( + "Compact<{}>", + format_type( + registry.resolve(compact.type_param.id).expect("compact type not found"), + registry + ) + ) + }, + TypeDef::BitSequence(_) => { + unimplemented!("bit sequence not currently supported") + }, + } + ); + + name +} + /// Processes a list of argument values for a specified contract function, /// wrapping each value in `Some(...)` or replacing it with `None` if the argument is optional /// @@ -153,26 +300,26 @@ pub fn process_function_args

( where P: AsRef, { - let parsed_args = args.into_iter().map(|arg| arg.replace(",", "")).collect::>(); let function = match function_type { FunctionType::Message => get_message(path, label)?, FunctionType::Constructor => get_constructor(path, label)?, }; - if parsed_args.len() != function.args.len() { + if args.len() != function.args.len() { return Err(Error::IncorrectArguments { expected: function.args.len(), - provided: parsed_args.len(), + provided: args.len(), }); } - Ok(parsed_args + Ok(args .into_iter() .zip(&function.args) - .map(|(arg, param)| match (param.type_name.as_str(), arg.is_empty()) { - ("Option", true) => "None".to_string(), /* If the argument is Option and empty, */ - // replace it with `None` - ("Option", false) => format!("Some({})", arg), /* If the argument is Option and not */ - // empty, wrap it in `Some(...)` - _ => arg, // If the argument is not Option, return it as is + .map(|(arg, param)| match (param.type_name.starts_with("Option<"), arg.is_empty()) { + // If the argument is Option and empty, replace it with `None` + (true, true) => "None".to_string(), + // If the argument is Option and not empty, wrap it in `Some(...)` + (true, false) => format!("Some({})", arg), + // If the argument is not Option, return it as is + _ => arg, }) .collect::>()) } @@ -207,7 +354,7 @@ mod tests { assert_eq!(message[2].args[0].label, "new_value".to_string()); assert_eq!(message[2].args[0].type_name, "bool".to_string()); assert_eq!(message[2].args[1].label, "number".to_string()); - assert_eq!(message[2].args[1].type_name, "Option".to_string()); + assert_eq!(message[2].args[1].type_name, "Option: None, Some(u32)".to_string()); Ok(()) } @@ -231,7 +378,7 @@ mod tests { assert_eq!(message.args[0].label, "new_value".to_string()); assert_eq!(message.args[0].type_name, "bool".to_string()); assert_eq!(message.args[1].label, "number".to_string()); - assert_eq!(message.args[1].type_name, "Option".to_string()); + assert_eq!(message.args[1].type_name, "Option: None, Some(u32)".to_string()); Ok(()) } @@ -264,7 +411,7 @@ mod tests { assert_eq!(constructor[1].args[0].label, "init_value".to_string()); assert_eq!(constructor[1].args[0].type_name, "bool".to_string()); assert_eq!(constructor[1].args[1].label, "number".to_string()); - assert_eq!(constructor[1].args[1].type_name, "Option".to_string()); + assert_eq!(constructor[1].args[1].type_name, "Option: None, Some(u32)".to_string()); Ok(()) } @@ -291,7 +438,7 @@ mod tests { assert_eq!(constructor.args[0].label, "init_value".to_string()); assert_eq!(constructor.args[0].type_name, "bool".to_string()); assert_eq!(constructor.args[1].label, "number".to_string()); - assert_eq!(constructor.args[1].type_name, "Option".to_string()); + assert_eq!(constructor.args[1].type_name, "Option: None, Some(u32)".to_string()); Ok(()) } From 5d76f1aaa6351fa00cc96c5946b37576ec617b9d Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:30:30 +0100 Subject: [PATCH 13/13] fix: logo doesn't show in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df7f72a9..a3a63f94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pop CLI - +