Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: support specify contract path input with or without -p flag #361

Merged
merged 16 commits into from
Jan 7, 2025
23 changes: 17 additions & 6 deletions crates/pop-cli/src/commands/build/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-3.0

use crate::cli::{self, Cli};
use crate::{
cli::{self, Cli},
common::builds::get_project_path,
};
use clap::{Args, Subcommand};
#[cfg(feature = "contract")]
use contract::BuildContract;
Expand All @@ -23,9 +26,12 @@ pub(crate) mod spec;
pub(crate) struct BuildArgs {
#[command(subcommand)]
pub command: Option<Command>,
/// Directory path for your project [default: current directory]
/// Directory path with flag for your project [default: current directory]
#[arg(long)]
pub(crate) path: Option<PathBuf>,
/// Directory path without flag for your project [default: current directory]
#[arg(value_name = "PATH", index = 1, conflicts_with = "path")]
pub(crate) path_pos: Option<PathBuf>,
/// The package to be built.
#[arg(short = 'p', long)]
pub(crate) package: Option<String>,
Expand All @@ -50,25 +56,29 @@ impl Command {
/// Executes the command.
pub(crate) fn execute(args: BuildArgs) -> anyhow::Result<&'static str> {
// If only contract feature enabled, build as contract
let project_path = get_project_path(args.path.clone(), args.path_pos.clone());

#[cfg(feature = "contract")]
if pop_contracts::is_supported(args.path.as_deref())? {
if pop_contracts::is_supported(project_path.as_deref().map(|v| v))? {
// All commands originating from root command are valid
let release = match args.profile {
Some(profile) => profile.into(),
None => args.release,
};
BuildContract { path: args.path, release }.execute()?;
BuildContract { path: project_path, release }.execute()?;
return Ok("contract");
}

// If only parachain feature enabled, build as parachain
#[cfg(feature = "parachain")]
if pop_parachains::is_supported(args.path.as_deref())? {
if pop_parachains::is_supported(project_path.as_deref().map(|v| v))? {
let profile = match args.profile {
Some(profile) => profile,
None => args.release.into(),
};
let temp_path = PathBuf::from("./");
BuildParachain {
path: args.path.unwrap_or_else(|| PathBuf::from("./")),
path: project_path.unwrap_or_else(|| temp_path).to_path_buf(),
package: args.package,
profile,
}
Expand Down Expand Up @@ -140,6 +150,7 @@ mod tests {
BuildArgs {
command: None,
path: Some(project_path.clone()),
path_pos: Some(project_path.clone()),
package: package.clone(),
release,
profile: Some(profile.clone()),
Expand Down
63 changes: 39 additions & 24 deletions crates/pop-cli/src/commands/call/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::{
cli::{self, traits::*},
common::{
builds::get_project_path,
contracts::has_contract_been_built,
wallet::{prompt_to_use_wallet, request_signature},
},
Expand All @@ -17,7 +18,7 @@ use pop_contracts::{
parse_account, set_up_call, CallExec, CallOpts, DefaultEnvironment, Verbosity,
};
use sp_weights::Weight;
use std::path::{Path, PathBuf};
use std::path::PathBuf;

const DEFAULT_URL: &str = "ws://localhost:9944/";
const DEFAULT_URI: &str = "//Alice";
Expand All @@ -28,6 +29,9 @@ pub struct CallContractCommand {
/// Path to the contract build directory or a contract artifact.
#[arg(short, long)]
path: Option<PathBuf>,
/// Directory path without flag for your project [default: current directory]
#[arg(value_name = "PATH", index = 1, conflicts_with = "path")]
pub(crate) path_pos: Option<PathBuf>,
/// The address of the contract to call.
#[arg(short, long, env = "CONTRACT")]
contract: Option<String>,
Expand Down Expand Up @@ -109,9 +113,13 @@ impl CallContractCommand {

fn display(&self) -> String {
let mut full_message = "pop call contract".to_string();

if let Some(path) = &self.path {
full_message.push_str(&format!(" --path {}", path.display()));
}
if let Some(path_pos) = &self.path_pos {
full_message.push_str(&format!(" --path {}", path_pos.display()));
}
if let Some(contract) = &self.contract {
full_message.push_str(&format!(" --contract {}", contract));
}
Expand Down Expand Up @@ -148,11 +156,12 @@ impl CallContractCommand {

/// If the contract has not been built, build it in release mode.
async fn ensure_contract_built(&self, cli: &mut impl Cli) -> Result<()> {
let project_path = get_project_path(self.path.clone(), self.path_pos.clone());
// Build the contract in release mode
cli.warning("NOTE: contract has not yet been built.")?;
let spinner = spinner();
spinner.start("Building contract in RELEASE mode...");
let result = match build_smart_contract(self.path.as_deref(), true, Verbosity::Quiet) {
let result = match build_smart_contract(project_path.as_deref(), true, Verbosity::Quiet) {
Ok(result) => result,
Err(e) => {
return Err(anyhow!(format!(
Expand Down Expand Up @@ -182,14 +191,18 @@ impl CallContractCommand {

/// Checks whether building the contract is required
fn is_contract_build_required(&self) -> bool {
self.path
let project_path = get_project_path(self.path.clone(), self.path_pos.clone());

project_path
.as_ref()
.map(|p| p.is_dir() && !has_contract_been_built(Some(p)))
.unwrap_or_default()
}

/// Configure the call based on command line arguments/call UI.
async fn configure(&mut self, cli: &mut impl Cli, repeat: bool) -> Result<()> {
let mut project_path = get_project_path(self.path.clone(), self.path_pos.clone());

// Show intro on first run.
if !repeat {
cli.intro("Call a contract")?;
Expand All @@ -201,16 +214,15 @@ impl CallContractCommand {
}

// Resolve path.
if self.path.is_none() {
if project_path.is_none() {
let input_path: String = cli
.input("Where is your project or contract artifact located?")
.placeholder("./")
.default_input("./")
.interact()?;
self.path = Some(PathBuf::from(input_path));
project_path = Some(PathBuf::from(input_path));
}
let contract_path = self
.path
let contract_path = project_path
.as_ref()
.expect("path is guaranteed to be set as input as prompted when None; qed");

Expand Down Expand Up @@ -357,15 +369,18 @@ impl CallContractCommand {
cli: &mut impl Cli,
prompt_to_repeat_call: bool,
) -> Result<()> {
let project_path = get_project_path(self.path.clone(), self.path_pos.clone());

let message = match &self.message {
Some(message) => message.to_string(),
None => {
return Err(anyhow!("Please specify the message to call."));
},
};
// Disable wallet signing and display warning if the call is read-only.
let path = PathBuf::from("./");
let message_metadata =
get_message(self.path.as_deref().unwrap_or_else(|| Path::new("./")), &message)?;
get_message(project_path.as_deref().unwrap_or_else(|| &path), &message)?;
if !message_metadata.mutates && self.use_wallet {
cli.warning("NOTE: Signing is not required for this read-only call. The '--use-wallet' flag will be ignored.")?;
self.use_wallet = false;
Expand All @@ -378,7 +393,7 @@ impl CallContractCommand {
},
};
let call_exec = match set_up_call(CallOpts {
path: self.path.clone(),
path: project_path,
contract,
message,
args: self.args.clone(),
Expand Down Expand Up @@ -564,6 +579,7 @@ mod tests {
// Contract deployed on Pop Network testnet, test get
CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: Some("get".to_string()),
args: vec![].to_vec(),
Expand Down Expand Up @@ -600,6 +616,7 @@ mod tests {

let mut call_config = CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: Some("flip".to_string()),
args: vec![].to_vec(),
Expand Down Expand Up @@ -637,6 +654,7 @@ mod tests {
// From .contract file
let mut call_config = CallContractCommand {
path: Some(current_dir.join("pop-contracts/tests/files/testing.contract")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: Some("flip".to_string()),
args: vec![].to_vec(),
Expand Down Expand Up @@ -723,6 +741,7 @@ mod tests {
// Contract deployed on Pop Network testnet, test get
let mut call_config = CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: Some("get".to_string()),
args: vec![].to_vec(),
Expand Down Expand Up @@ -770,10 +789,6 @@ mod tests {
Some(items),
1, // "get" message
)
.expect_input(
"Where is your project or contract artifact located?",
temp_dir.path().join("testing").display().to_string(),
)
.expect_input(
"Where is your contract deployed?",
"wss://rpc1.paseo.popnetwork.xyz".into(),
Expand All @@ -789,6 +804,7 @@ mod tests {

let mut call_config = CallContractCommand {
path: None,
path_pos: Some(temp_dir.path().join("testing")),
contract: None,
message: None,
args: vec![].to_vec(),
Expand Down Expand Up @@ -818,7 +834,7 @@ mod tests {
assert!(!call_config.dry_run);
assert_eq!(call_config.display(), format!(
"pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message get --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice",
temp_dir.path().join("testing").display().to_string(),
temp_dir.path().join("testing").display().to_string()
));

cli.verify()
Expand All @@ -844,10 +860,6 @@ mod tests {
];
// The inputs are processed in reverse order.
let mut cli = MockCli::new()
.expect_input(
"Where is your project or contract artifact located?",
temp_dir.path().join("testing").display().to_string(),
)
.expect_input(
"Where is your contract deployed?",
"wss://rpc1.paseo.popnetwork.xyz".into(),
Expand Down Expand Up @@ -876,6 +888,7 @@ mod tests {

let mut call_config = CallContractCommand {
path: None,
path_pos: Some(temp_dir.path().join("testing")),
contract: None,
message: None,
args: vec![].to_vec(),
Expand Down Expand Up @@ -908,7 +921,7 @@ mod tests {
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/ --use-wallet --execute",
temp_dir.path().join("testing").display().to_string(),
temp_dir.path().join("testing").display().to_string()
));

cli.verify()
Expand Down Expand Up @@ -941,10 +954,6 @@ mod tests {
Some(items),
2, // "specific_flip" message
)
.expect_input(
"Where is your project or contract artifact located?",
temp_dir.path().join("testing").display().to_string(),
)
.expect_input(
"Where is your contract deployed?",
"wss://rpc1.paseo.popnetwork.xyz".into(),
Expand All @@ -964,6 +973,7 @@ mod tests {

let mut call_config = CallContractCommand {
path: None,
path_pos: Some(temp_dir.path().join("testing")),
contract: None,
message: None,
args: vec![].to_vec(),
Expand Down Expand Up @@ -996,7 +1006,7 @@ mod tests {
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",
temp_dir.path().join("testing").display().to_string(),
temp_dir.path().join("testing").display().to_string()
));

cli.verify()
Expand All @@ -1023,6 +1033,7 @@ mod tests {
// Test the path is a folder with an invalid build.
let mut command = CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: None,
message: None,
args: vec![].to_vec(),
Expand Down Expand Up @@ -1073,6 +1084,7 @@ mod tests {
assert!(matches!(
CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: None,
args: vec![].to_vec(),
Expand All @@ -1092,6 +1104,7 @@ mod tests {
assert!(matches!(
CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: None,
message: Some("get".to_string()),
args: vec![].to_vec(),
Expand All @@ -1116,6 +1129,7 @@ mod tests {
let temp_dir = new_environment("testing")?;
let call_config = CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: None,
args: vec![].to_vec(),
Expand Down Expand Up @@ -1147,6 +1161,7 @@ mod tests {
let temp_dir = new_environment("testing")?;
let call_config = CallContractCommand {
path: Some(temp_dir.path().join("testing")),
path_pos: None,
contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()),
message: None,
args: vec![].to_vec(),
Expand Down
Loading
Loading