diff --git a/Cargo.lock b/Cargo.lock index 3c6275c6..686cdaf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5463,6 +5463,7 @@ dependencies = [ "subxt 0.34.0", "subxt-signer 0.34.0", "tempfile", + "tokio", "url", ] diff --git a/crates/pop-contracts/Cargo.toml b/crates/pop-contracts/Cargo.toml index fe795817..57024a5c 100644 --- a/crates/pop-contracts/Cargo.toml +++ b/crates/pop-contracts/Cargo.toml @@ -22,4 +22,5 @@ contract-build = { version = "4.0.2" } contract-extrinsics = { version = "4.0.0-rc.3"} [dev-dependencies] -tempfile.workspace = true \ No newline at end of file +tempfile.workspace = true +tokio.workspace = true \ No newline at end of file diff --git a/crates/pop-contracts/src/call.rs b/crates/pop-contracts/src/call.rs index e0b271f9..dbe081e5 100644 --- a/crates/pop-contracts/src/call.rs +++ b/crates/pop-contracts/src/call.rs @@ -43,8 +43,8 @@ pub async fn set_up_call( ) -> anyhow::Result> { let token_metadata = TokenMetadata::query::(&call_opts.url).await?; let manifest_path = get_manifest_path(&call_opts.path)?; - let signer = create_signer(&call_opts.suri)?; + let extrinsic_opts = ExtrinsicOptsBuilder::new(signer) .manifest_path(Some(manifest_path)) .url(call_opts.url.clone()) @@ -130,3 +130,98 @@ pub async fn call_smart_contract( display_events.display_events::(Verbosity::Default, &token_metadata)?; Ok(output) } + +#[cfg(feature = "unit_contract")] +#[cfg(test)] +mod tests { + use super::*; + use crate::{build_smart_contract, create_smart_contract}; + use anyhow::{Error, Result}; + use std::fs; + use tempfile::TempDir; + + const CONTRACTS_NETWORK_URL: &str = "wss://rococo-contracts-rpc.polkadot.io"; + + fn generate_smart_contract_test_environment() -> Result { + let temp_dir = tempfile::tempdir().expect("Could not create temp dir"); + let temp_contract_dir = temp_dir.path().join("test_contract"); + fs::create_dir(&temp_contract_dir)?; + let result = + create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path()); + assert!(result.is_ok(), "Contract test environment setup failed"); + + Ok(temp_dir) + } + fn build_smart_contract_test_environment(temp_dir: &TempDir) -> Result<(), Error> { + build_smart_contract(&Some(temp_dir.path().join("test_contract")))?; + Ok(()) + } + + #[tokio::test] + async fn test_set_up_call() -> Result<(), Error> { + let temp_dir = generate_smart_contract_test_environment()?; + build_smart_contract_test_environment(&temp_dir)?; + + let call_opts = CallOpts { + path: Some(temp_dir.path().join("test_contract")), + contract: "5CLPm1CeUvJhZ8GCDZCR7nWZ2m3XXe4X5MtAQK69zEjut36A".to_string(), + message: "get".to_string(), + args: [].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + suri: "//Alice".to_string(), + execute: false, + }; + let call = set_up_call(call_opts).await; + assert!(call.is_ok()); + assert_eq!(call.unwrap().message(), "get"); + + Ok(()) + } + + #[tokio::test] + async fn test_set_up_call_error_contract_not_build() -> Result<(), Error> { + let temp_dir = generate_smart_contract_test_environment()?; + let call_opts = CallOpts { + path: Some(temp_dir.path().join("test_contract")), + contract: "5CLPm1CeUvJhZ8GCDZCR7nWZ2m3XXe4X5MtAQK69zEjut36A".to_string(), + message: "get".to_string(), + args: [].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + 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."); + + Ok(()) + } + #[tokio::test] + async fn test_set_up_call_fails_no_smart_contract_folder() -> Result<(), Error> { + let call_opts = CallOpts { + path: None, + contract: "5CLPm1CeUvJhZ8GCDZCR7nWZ2m3XXe4X5MtAQK69zEjut36A".to_string(), + message: "get".to_string(), + args: [].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + 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"); + + Ok(()) + } +} diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index e6c9b761..78dd7aec 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -68,24 +68,24 @@ pub async fn dry_run_gas_estimate_instantiate( ) -> anyhow::Result { let instantiate_result = instantiate_exec.instantiate_dry_run().await?; match instantiate_result.result { - Ok(_) => { - // use user specified values where provided, otherwise use the estimates - let ref_time = instantiate_exec - .args() - .gas_limit() - .unwrap_or_else(|| instantiate_result.gas_required.ref_time()); - let proof_size = instantiate_exec - .args() - .proof_size() - .unwrap_or_else(|| instantiate_result.gas_required.proof_size()); - Ok(Weight::from_parts(ref_time, proof_size)) - } - Err(ref _err) => { - Err(anyhow::anyhow!( + Ok(_) => { + // use user specified values where provided, otherwise use the estimates + let ref_time = instantiate_exec + .args() + .gas_limit() + .unwrap_or_else(|| instantiate_result.gas_required.ref_time()); + let proof_size = instantiate_exec + .args() + .proof_size() + .unwrap_or_else(|| instantiate_result.gas_required.proof_size()); + Ok(Weight::from_parts(ref_time, proof_size)) + }, + Err(ref _err) => { + Err(anyhow::anyhow!( "Pre-submission dry-run failed. Add gas_limit and proof_size manually to skip this step." )) - } - } + }, + } } pub async fn instantiate_smart_contract( @@ -95,3 +95,78 @@ pub async fn instantiate_smart_contract( let instantiate_result = instantiate_exec.instantiate(Some(gas_limit)).await?; Ok(instantiate_result.contract_address.to_string()) } + +#[cfg(feature = "unit_contract")] +#[cfg(test)] +mod tests { + use super::*; + use crate::{build_smart_contract, create_smart_contract}; + use anyhow::{Error, Result}; + use std::fs; + use tempfile::TempDir; + use url::Url; + + const CONTRACTS_NETWORK_URL: &str = "wss://rococo-contracts-rpc.polkadot.io"; + + fn generate_smart_contract_test_environment() -> Result { + let temp_dir = tempfile::tempdir().expect("Could not create temp dir"); + let temp_contract_dir = temp_dir.path().join("test_contract"); + fs::create_dir(&temp_contract_dir)?; + let result = + create_smart_contract("test_contract".to_string(), temp_contract_dir.as_path()); + assert!(result.is_ok(), "Contract test environment setup failed"); + + Ok(temp_dir) + } + fn build_smart_contract_test_environment(temp_dir: &TempDir) -> Result<(), Error> { + build_smart_contract(&Some(temp_dir.path().join("test_contract")))?; + Ok(()) + } + + #[tokio::test] + async fn test_set_up_deployment() -> Result<(), Error> { + let temp_dir = generate_smart_contract_test_environment()?; + build_smart_contract_test_environment(&temp_dir)?; + + let call_opts = UpOpts { + path: Some(temp_dir.path().join("test_contract")), + constructor: "new".to_string(), + args: ["false".to_string()].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + suri: "//Alice".to_string(), + salt: None, + }; + let result = set_up_deployment(call_opts).await; + assert!(result.is_ok()); + assert_eq!(result.unwrap().url(), "wss://rococo-contracts-rpc.polkadot.io:443/"); + Ok(()) + } + + #[tokio::test] + async fn test_dry_run_gas_estimate_instantiate() -> Result<(), Error> { + let temp_dir = generate_smart_contract_test_environment()?; + build_smart_contract_test_environment(&temp_dir)?; + + let call_opts = UpOpts { + path: Some(temp_dir.path().join("test_contract")), + constructor: "new".to_string(), + args: ["false".to_string()].to_vec(), + value: "0".to_string(), + gas_limit: None, + proof_size: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + suri: "//Alice".to_string(), + salt: None, + }; + let instantiate_exec = set_up_deployment(call_opts).await; + + let result = dry_run_gas_estimate_instantiate(&instantiate_exec.unwrap()).await; + assert!(result.is_ok()); + assert_eq!(result.unwrap(), Weight::from_parts(140492887, 16689)); + + Ok(()) + } +}