use crate::{
artifacts::Source,
error::{Result, SolcError},
utils, CompilerInput, CompilerOutput,
};
use semver::{Version, VersionReq};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
fmt,
io::BufRead,
path::{Path, PathBuf},
process::{Command, Output, Stdio},
str::FromStr,
};
pub mod contracts;
pub mod many;
pub mod output;
pub mod project;
/// The name of the `solc` binary on the system
pub const SOLC: &str = "solc";
/// Support for configuring the EVM version
///
pub const CONSTANTINOPLE_SOLC: Version = Version::new(0, 4, 21);
/// Petersburg support
///
pub const PETERSBURG_SOLC: Version = Version::new(0, 5, 5);
/// Istanbul support
///
pub const ISTANBUL_SOLC: Version = Version::new(0, 5, 14);
/// Berlin support
///
pub const BERLIN_SOLC: Version = Version::new(0, 8, 5);
/// London support
///
pub const LONDON_SOLC: Version = Version::new(0, 8, 7);
#[cfg(any(test, feature = "tests"))]
use std::sync::Mutex;
#[cfg(any(test, feature = "tests"))]
#[allow(unused)]
static LOCK: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(|| Mutex::new(()));
/// take the lock in tests, we use this to enforce that
/// a test does not run while a compiler version is being installed
///
/// This ensures that only one thread installs a missing `solc` exe.
/// Instead of taking this lock in `Solc::blocking_install`, the lock should be taken before
/// installation is detected.
#[cfg(any(test, feature = "tests"))]
#[allow(unused)]
pub(crate) fn take_solc_installer_lock() -> std::sync::MutexGuard<'static, ()> {
LOCK.lock().unwrap()
}
/// A list of upstream Solc releases, used to check which version
/// we should download.
/// The boolean value marks whether there was an error accessing the release list
#[cfg(all(feature = "svm-solc"))]
pub static RELEASES: once_cell::sync::Lazy<(svm::Releases, Vec, bool)> =
once_cell::sync::Lazy::new(|| {
match serde_json::from_str::(svm_builds::RELEASE_LIST_JSON) {
Ok(releases) => {
let sorted_versions = releases.clone().into_versions();
(releases, sorted_versions, true)
}
Err(err) => {
tracing::error!("{:?}", err);
(svm::Releases::default(), Vec::new(), false)
}
}
});
/// A `Solc` version is either installed (available locally) or can be downloaded, from the remote
/// endpoint
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SolcVersion {
Installed(Version),
Remote(Version),
}
impl SolcVersion {
/// Whether this version is installed
pub fn is_installed(&self) -> bool {
matches!(self, SolcVersion::Installed(_))
}
}
impl AsRef for SolcVersion {
fn as_ref(&self) -> &Version {
match self {
SolcVersion::Installed(v) | SolcVersion::Remote(v) => v,
}
}
}
impl From for Version {
fn from(s: SolcVersion) -> Version {
match s {
SolcVersion::Installed(v) | SolcVersion::Remote(v) => v,
}
}
}
impl fmt::Display for SolcVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
/// Abstraction over `solc` command line utility
///
/// Supports sync and async functions.
///
/// By default the solc path is configured as follows, with descending priority:
/// 1. `SOLC_PATH` environment variable
/// 2. [svm](https://github.com/roynalnaruto/svm-rs)'s `global_version` (set via `svm use `), stored at `/.global_version`
/// 3. `solc` otherwise
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Solc {
/// Path to the `solc` executable
pub solc: PathBuf,
/// Additional arguments passed to the `solc` exectuable
pub args: Vec,
}
impl Default for Solc {
fn default() -> Self {
if let Ok(solc) = std::env::var("SOLC_PATH") {
return Solc::new(solc)
}
#[cfg(not(target_arch = "wasm32"))]
{
if let Some(solc) = Solc::svm_global_version()
.and_then(|vers| Solc::find_svm_installed_version(&vers.to_string()).ok())
.flatten()
{
return solc
}
}
Solc::new(SOLC)
}
}
impl fmt::Display for Solc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.solc.display())?;
if !self.args.is_empty() {
write!(f, " {}", self.args.join(" "))?;
}
Ok(())
}
}
impl Solc {
/// A new instance which points to `solc`
pub fn new(path: impl Into) -> Self {
Solc { solc: path.into(), args: Vec::new() }
}
/// Adds an argument to pass to the `solc` command.
#[must_use]
pub fn arg>(mut self, arg: T) -> Self {
self.args.push(arg.into());
self
}
/// Adds multiple arguments to pass to the `solc`.
#[must_use]
pub fn args(mut self, args: I) -> Self
where
I: IntoIterator,
S: Into,
{
for arg in args {
self = self.arg(arg);
}
self
}
/// Returns the directory in which [svm](https://github.com/roynalnaruto/svm-rs) stores all versions
///
/// This will be `~/.svm` on unix
#[cfg(not(target_arch = "wasm32"))]
pub fn svm_home() -> Option {
home::home_dir().map(|dir| dir.join(".svm"))
}
/// Returns the `semver::Version` [svm](https://github.com/roynalnaruto/svm-rs)'s `.global_version` is currently set to.
/// `global_version` is configured with (`svm use `)
///
/// This will read the version string (eg: "0.8.9") that the `~/.svm/.global_version` file
/// contains
#[cfg(not(target_arch = "wasm32"))]
pub fn svm_global_version() -> Option {
let version =
std::fs::read_to_string(Self::svm_home().map(|p| p.join(".global_version"))?).ok()?;
Version::parse(&version).ok()
}
/// Returns the list of all solc instances installed at `SVM_HOME`
#[cfg(not(target_arch = "wasm32"))]
pub fn installed_versions() -> Vec {
if let Some(home) = Self::svm_home() {
utils::installed_versions(home)
.unwrap_or_default()
.into_iter()
.map(SolcVersion::Installed)
.collect()
} else {
Vec::new()
}
}
/// Returns the list of all versions that are available to download and marking those which are
/// already installed.
#[cfg(all(feature = "svm-solc"))]
pub fn all_versions() -> Vec {
let mut all_versions = Self::installed_versions();
let mut uniques = all_versions
.iter()
.map(|v| {
let v = v.as_ref();
(v.major, v.minor, v.patch)
})
.collect::>();
all_versions.extend(
RELEASES
.1
.clone()
.into_iter()
.filter(|v| uniques.insert((v.major, v.minor, v.patch)))
.map(SolcVersion::Remote),
);
all_versions.sort_unstable();
all_versions
}
/// Returns the path for a [svm](https://github.com/roynalnaruto/svm-rs) installed version.
///
/// # Example
/// ```no_run
/// # fn main() -> Result<(), Box> {
/// use ethers_solc::Solc;
/// let solc = Solc::find_svm_installed_version("0.8.9").unwrap();
/// assert_eq!(solc, Some(Solc::new("~/.svm/0.8.9/solc-0.8.9")));
/// # Ok(())
/// # }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn find_svm_installed_version(version: impl AsRef) -> Result