diff --git a/README.md b/README.md index df7f72a9..a3a63f94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pop CLI - +
diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index 1a559ebc..8db1cc63 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..,)] 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,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)); @@ -176,7 +177,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!( @@ -230,11 +231,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.starts_with("Option<") { + input = input.default_input(""); + } + contract_args.push(input.interact()?); } self.args = contract_args; @@ -671,6 +676,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 +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 --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 +721,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 +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 --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(), )); @@ -754,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:", @@ -774,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(), )); @@ -798,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); @@ -809,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-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index d357d958..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..)] + #[clap(long, num_args = 0..,)] args: Vec, /// Transfers an initial balance to the instantiated contract. #[clap(name = "value", long, default_value = "0")] @@ -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/call/mod.rs b/crates/pop-contracts/src/call.rs similarity index 93% rename from crates/pop-contracts/src/call/mod.rs rename to crates/pop-contracts/src/call.rs index 2c363301..7a964c38 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::{process_function_args, FunctionType}, 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. @@ -53,7 +52,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 +66,17 @@ pub async fn set_up_call( parse_balance(&call_opts.value)?; let contract: ::AccountId = parse_account(&call_opts.contract)?; + // Process the argument values input by the user. + 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 = 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/call/metadata.rs b/crates/pop-contracts/src/call/metadata.rs deleted file mode 100644 index 605d0620..00000000 --- a/crates/pop-contracts/src/call/metadata.rs +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -use crate::errors::Error; -use contract_extrinsics::ContractArtifacts; -use contract_transcode::ink_metadata::MessageParamSpec; -use scale_info::form::PortableForm; -use std::path::Path; - -#[derive(Clone, PartialEq, Eq)] -/// Describes a contract message. -pub struct Param { - /// The label of the parameter. - pub label: String, - /// The type name of the parameter. - pub type_name: String, -} -#[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, -} - -/// 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> { - 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 messages: Vec = Vec::new(); - for message in transcoder.metadata().spec().messages() { - messages.push(Message { - 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) -} -// Parse the message parameters into a vector of argument labels. -fn process_args(message_params: &[MessageParamSpec]) -> Vec { - let mut args: Vec = Vec::new(); - for arg in message_params { - args.push(Param { - label: arg.label().to_string(), - type_name: arg.ty().display_name().to_string(), - }); - } - args -} - -#[cfg(test)] -mod tests { - use std::env; - - use super::*; - use crate::{mock_build_process, new_environment}; - use anyhow::Result; - - #[test] - fn get_messages_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 message = get_messages(&temp_dir.path().join("testing"))?; - assert_eq!(message.len(), 3); - assert_eq!(message[0].label, "flip"); - assert_eq!(message[0].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."); - assert_eq!(message[1].label, "get"); - assert_eq!(message[1].docs, " Simply returns the current value of our `bool`."); - 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[0].label, "new_value".to_string()); - assert_eq!(message[2].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..3d19d679 100644 --- a/crates/pop-contracts/src/errors.rs +++ b/crates/pop-contracts/src/errors.rs @@ -28,6 +28,10 @@ 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}")] InvalidName(String), #[error("IO error: {0}")] @@ -36,6 +40,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}")] @@ -44,12 +50,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("Incorrect number of arguments provided. Expecting {expected}, {provided} provided")] + IncorrectArguments { expected: usize, provided: usize }, } diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index a3ff0175..14228399 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, ContractFunction}, + signer::parse_hex_bytes, +}; diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index 6d2f0ec0..2b304123 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::{process_function_args, FunctionType}, signer::create_signer, }, }; @@ -62,10 +63,17 @@ 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 = 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) .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/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs new file mode 100644 index 00000000..f0dd2b8e --- /dev/null +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::errors::Error; +use contract_extrinsics::ContractArtifacts; +use contract_transcode::ink_metadata::MessageParamSpec; +use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef, TypeDefPrimitive}; +use std::path::Path; + +/// Describes a parameter. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Param { + /// The label of the parameter. + pub label: String, + /// The type name of the parameter. + pub type_name: String, +} + +/// Describes a contract function. +#[derive(Clone, PartialEq, Eq)] +pub struct ContractFunction { + /// The label of the function. + pub label: String, + /// If the function accepts any `value` from the caller. + pub payable: bool, + /// The parameters of the deployment handler. + pub args: Vec, + /// The function documentation. + pub docs: String, + /// 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, +} + +/// Specifies the type of contract funtion, either a constructor or a message. +#[derive(Clone, PartialEq, Eq)] +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> { + 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 metadata = transcoder.metadata(); + Ok(metadata + .spec() + .messages() + .iter() + .map(|message| ContractFunction { + label: message.label().to_string(), + mutates: message.mutates(), + payable: message.payable(), + args: process_args(message.args(), metadata.registry()), + docs: message.docs().join(" "), + default: *message.default(), + }) + .collect()) +} + +/// 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. +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())) +} + +/// 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 metadata = transcoder.metadata(); + Ok(metadata + .spec() + .constructors() + .iter() + .map(|constructor| ContractFunction { + label: constructor.label().to_string(), + payable: *constructor.payable(), + args: process_args(constructor.args(), metadata.registry()), + docs: constructor.docs().join(" "), + default: *constructor.default(), + mutates: true, + }) + .collect()) +} + +/// 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| 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], + registry: &PortableRegistry, +) -> Vec { + let mut args: Vec = Vec::new(); + for arg in params { + // 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 +/// +/// # Arguments +/// * `path` - Location path of the project. +/// * `label` - Label of the contract message to retrieve. +/// * `args` - Argument values provided by the user. +pub fn process_function_args

( + path: P, + label: &str, + args: Vec, + function_type: FunctionType, +) -> Result, Error> +where + P: AsRef, +{ + 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: function.args.len(), + provided: args.len(), + }); + } + Ok(args + .into_iter() + .zip(&function.args) + .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::>()) +} + +#[cfg(test)] +mod tests { + use std::env; + + use super::*; + use crate::{mock_build_process, new_environment}; + use anyhow::Result; + + #[test] + fn get_messages_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 message = get_messages(&temp_dir.path().join("testing"))?; + assert_eq!(message.len(), 3); + assert_eq!(message[0].label, "flip"); + assert_eq!(message[0].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."); + assert_eq!(message[1].label, "get"); + assert_eq!(message[1].docs, " Simply returns the current value of our `bool`."); + 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(), 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: None, Some(u32)".to_string()); + Ok(()) + } + + #[test] + fn get_message_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_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(), 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: None, Some(u32)".to_string()); + 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(), 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[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: None, Some(u32)".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"), "default")?; + assert_eq!(constructor.label, "default"); + assert_eq!( + constructor.docs, + "Constructor that initializes the `bool` value to `false`. Constructors can delegate to other constructors." + ); + // 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: None, Some(u32)".to_string()); + Ok(()) + } + + #[test] + 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"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + assert!(matches!( + 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_function_args( + temp_dir.path().join("testing"), + "specific_flip", + Vec::new(), + FunctionType::Message + ), + Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 + )); + assert_eq!( + process_function_args( + temp_dir.path().join("testing"), + "specific_flip", + ["true".to_string(), "2".to_string()].to_vec(), + FunctionType::Message + )?, + ["true".to_string(), "Some(2)".to_string()] + ); + assert_eq!( + process_function_args( + temp_dir.path().join("testing"), + "specific_flip", + ["true".to_string(), "".to_string()].to_vec(), + FunctionType::Message + )?, + ["true".to_string(), "None".to_string()] + ); + + // Test constructors + assert!(matches!( + 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_function_args( + temp_dir.path().join("testing"), + "default", + Vec::new(), + FunctionType::Constructor + ), + Err(Error::IncorrectArguments {expected, provided }) if expected == 2 && provided == 0 + )); + assert_eq!( + process_function_args( + temp_dir.path().join("testing"), + "default", + ["true".to_string(), "2".to_string()].to_vec(), + FunctionType::Constructor + )?, + ["true".to_string(), "Some(2)".to_string()] + ); + assert_eq!( + process_function_args( + temp_dir.path().join("testing"), + "default", + ["true".to_string(), "".to_string()].to_vec(), + FunctionType::Constructor + )?, + ["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 5fb54f3c..fbd8e6fb 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":"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 ed230c98..61921986 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": "0xf4f0cafd08d8e362141b3c64e3c651ad6a38225dbf0b66f691c15bb0ea00eac3", "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,40 @@ } }, { - "id": 5, + "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" @@ -134,7 +180,7 @@ }, { "name": "E", - "type": 4 + "type": 5 } ], "def": { @@ -153,7 +199,7 @@ "name": "Err", "fields": [ { - "type": 4 + "type": 5 } ], "index": 1 @@ -164,7 +210,7 @@ } }, { - "id": 6, + "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" @@ -315,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": 2, + "type": 3, "displayName": [ "ink_primitives", "ConstructorResult" @@ -339,7 +405,7 @@ "payable": false, "args": [], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -359,7 +425,7 @@ "payable": false, "args": [], "returnType": { - "type": 5, + "type": 7, "displayName": [ "ink", "MessageResult" @@ -384,10 +450,19 @@ "bool" ] } + }, + { + "label": "number", + "type": { + "type": 6, + "displayName": [ + "Option" + ] + } } ], "returnType": { - "type": 2, + "type": 3, "displayName": [ "ink", "MessageResult" @@ -403,7 +478,7 @@ "events": [], "docs": [], "lang_error": { - "type": 4, + "type": 5, "displayName": [ "ink", "LangError" @@ -411,37 +486,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" ]