Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 78 additions & 16 deletions packages/cw-orch-core/src/contract/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ mod artifacts_dir {
pub fn find_wasm_path(&self, name: &str) -> Result<WasmPath, CwEnvError> {
self.find_wasm_path_with_build_postfix(name, <BuildPostfix>::None)
}
/// Find a WASM file in the artifacts directory that contains the given contracts crates.io label.
/// This function normalizes crates.io labels by stripping the "crates.io:" prefix and converting hyphens to underscores.
/// Example: "crates.io:bs721-account" becomes "bs721_account"
pub fn find_wasm_path_from_crates_label(&self, crates_label: &str) -> Result<WasmPath, CwEnvError> {
let normalized_name = normalize_contract_name(crates_label);
self.find_wasm_path_with_build_postfix(&normalized_name, <BuildPostfix>::None)
}

/// Find a WASM file in the artifacts directory that contains the given contract name AND build post-fix.
/// If a build with the post-fix is not found, the default build will be used.
Expand All @@ -172,6 +179,10 @@ mod artifacts_dir {
let mut default_wasm = None;
let mut arm_default_wasm = None;

// Collect all potential matches, prioritizing exact matches
let mut exact_default_wasm = None;
let mut exact_arm_default_wasm = None;

for entry in fs::read_dir(self.path())?.flatten() {
let path = entry.path();
// Skip if not a wasm file
Expand All @@ -180,26 +191,45 @@ mod artifacts_dir {
}

let file_name = path.file_name().unwrap_or_default().to_string_lossy();
// Wasm with build postfix, non-ARM

// Wasm with build postfix, non-ARM (highest priority)
if is_artifact_with_build_postfix(&file_name, name, &build_postfix) {
wasm_with_postfix = Some(file_name.into_owned());
// As it's highest priority we just the loop end here
break;
// Prefer exact matches even within the same priority level
if wasm_with_postfix.is_none() || is_exact_match(&file_name, name) {
wasm_with_postfix = Some(file_name.clone().into_owned());
if is_exact_match(&file_name, name) {
break; // Found exact match, no need to continue
}
}
}

// Check other valid filenames
if is_arm_artifact_with_build_postfix(&file_name, name, &build_postfix) {
// Wasm with build postfix, ARM
arm_wasm_with_postfix = Some(file_name.into_owned())
} else if is_default_artifact(&file_name, name) {
// Wasm without build postfix, non-ARM
default_wasm = Some(file_name.into_owned())
} else if is_default_arm_artifact(&file_name, name) {
// Wasm without build postfix, ARM
arm_default_wasm = Some(file_name.into_owned())
// Wasm with build postfix, ARM
else if is_arm_artifact_with_build_postfix(&file_name, name, &build_postfix) {
if arm_wasm_with_postfix.is_none() || is_exact_arm_match(&file_name, name) {
arm_wasm_with_postfix = Some(file_name.into_owned());
}
}
// Wasm without build postfix, non-ARM
else if is_default_artifact(&file_name, name) {
if file_name == format!("{name}.wasm") {
exact_default_wasm = Some(file_name.into_owned());
} else if default_wasm.is_none() {
default_wasm = Some(file_name.into_owned());
}
}
// Wasm without build postfix, ARM
else if is_default_arm_artifact(&file_name, name) {
if file_name == format!("{name}{ARM_POSTFIX}.wasm") {
exact_arm_default_wasm = Some(file_name.into_owned());
} else if arm_default_wasm.is_none() {
arm_default_wasm = Some(file_name.into_owned());
}
}
}

// Prefer exact matches within each category
let default_wasm = exact_default_wasm.or(default_wasm);
let arm_default_wasm = exact_arm_default_wasm.or(arm_default_wasm);

let path_str = wasm_with_postfix
.or(arm_wasm_with_postfix)
.or(default_wasm)
Expand All @@ -215,7 +245,21 @@ mod artifacts_dir {
}

fn is_artifact(file_name: &str, contract_name: &str) -> bool {
file_name.contains(contract_name)
// More precise matching to avoid false positives like "someframework_niece.wasm" when looking for "someframework"
// Support patterns like: contract_name.wasm, contract_name-suffix.wasm, contract_name_suffix.wasm
let stem = file_name.strip_suffix(".wasm").unwrap_or(file_name);

// Exact match
if stem == contract_name {
return true;
}

// Starts with contract_name followed by delimiter (underscore or hyphen)
if stem.starts_with(&format!("{contract_name}_")) || stem.starts_with(&format!("{contract_name}-")) {
return true;
}

false
}

fn is_default_artifact(file_name: &str, contract_name: &str) -> bool {
Expand Down Expand Up @@ -243,4 +287,22 @@ mod artifacts_dir {
is_artifact(file_name, contract_name)
&& file_name.ends_with(format!("{build_postfix}{ARM_POSTFIX}.wasm").as_str())
}

fn is_exact_match(file_name: &str, contract_name: &str) -> bool {
let stem = file_name.strip_suffix(".wasm").unwrap_or(file_name);
stem == contract_name
}

fn is_exact_arm_match(file_name: &str, contract_name: &str) -> bool {
let stem = file_name.strip_suffix(".wasm").unwrap_or(file_name);
stem == format!("{contract_name}{ARM_POSTFIX}")
}

/// Normalize a contract name from a crates.io label to the format used by the Rust compiler.
/// Strips "crates.io:" prefix if present and converts hyphens to underscores.
/// Example: "crates.io:bs721-account" -> "bs721_account"
fn normalize_contract_name(name: &str) -> String {
let stripped = name.strip_prefix("crates.io:").unwrap_or(name);
stripped.replace('-', "_")
}
}
Loading