Skip to main content

ethrex_sdk_contract_utils/
compile.rs

1use std::{
2    path::{Path, PathBuf},
3    process::Command,
4};
5
6#[derive(Debug, thiserror::Error)]
7pub enum ContractCompilationError {
8    #[error("The path is not a valid utf-8 string")]
9    FailedToGetStringFromPath,
10    #[error("Deployer compilation error: {0}")]
11    CompilationError(String),
12}
13
14pub fn compile_contract(
15    output_dir: &Path,
16    contract_path: &Path,
17    runtime_bin: bool,
18    abi_json: bool,
19    remappings: Option<&[(&str, PathBuf)]>,
20    allow_paths: &[&Path],
21    optimize_runs: Option<u64>,
22) -> Result<(), ContractCompilationError> {
23    let bin_flag = if runtime_bin {
24        "--bin-runtime"
25    } else {
26        "--bin"
27    };
28
29    let mut cmd = Command::new("solc");
30    cmd.arg(bin_flag);
31
32    if abi_json {
33        cmd.arg("--abi");
34    }
35
36    apply_remappings(&mut cmd, remappings)?;
37
38    if let Some(optimize_runs) = optimize_runs {
39        cmd.arg("--optimize")
40            .arg("--optimize-runs")
41            .arg(format!("{optimize_runs}"));
42    }
43
44    cmd.arg(
45        contract_path
46            .to_str()
47            .ok_or(ContractCompilationError::FailedToGetStringFromPath)?,
48    )
49    .arg("--via-ir")
50    .arg("-o")
51    .arg(
52        output_dir
53            .join("solc_out")
54            .to_str()
55            .ok_or(ContractCompilationError::FailedToGetStringFromPath)?,
56    )
57    .arg("--overwrite")
58    .arg("--no-cbor-metadata");
59
60    if !allow_paths.is_empty() {
61        apply_allow_paths(&mut cmd, allow_paths)?;
62    }
63
64    let cmd_succeeded = cmd
65        .spawn()
66        .map_err(|err| {
67            ContractCompilationError::CompilationError(format!("Failed to spawn solc: {err}"))
68        })?
69        .wait()
70        .map_err(|err| {
71            ContractCompilationError::CompilationError(format!("Failed to wait for solc: {err}"))
72        })?
73        .success();
74
75    if !cmd_succeeded {
76        return Err(ContractCompilationError::CompilationError(format!(
77            "Failed to compile {contract_path:?}"
78        )));
79    }
80
81    Ok(())
82}
83
84fn apply_remappings(
85    cmd: &mut Command,
86    remappings: Option<&[(&str, PathBuf)]>,
87) -> Result<(), ContractCompilationError> {
88    if let Some(remaps) = remappings {
89        for (prefix, path) in remaps {
90            let path_str = path
91                .to_str()
92                .ok_or(ContractCompilationError::FailedToGetStringFromPath)?;
93            cmd.arg(format!("{prefix}={path_str}"));
94        }
95    }
96    Ok(())
97}
98
99fn apply_allow_paths(
100    cmd: &mut Command,
101    allow_paths: &[&Path],
102) -> Result<(), ContractCompilationError> {
103    cmd.arg("--allow-paths");
104    let joined_paths = allow_paths
105        .iter()
106        .map(|p| {
107            p.to_str()
108                .ok_or(ContractCompilationError::FailedToGetStringFromPath)
109        })
110        .collect::<Result<Vec<_>, _>>()?
111        .join(",");
112    cmd.arg(joined_paths);
113    Ok(())
114}