1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use anyhow::{anyhow, bail, format_err, Result};
use std::{
collections::BTreeMap,
ffi::OsStr,
fs::{self},
io::Write,
path::{Path, PathBuf},
process::{Command, Stdio},
};
const OPTIMIZED_YUL_MARKER: &str = "\nPretty printed source:";
const HEX_OUTPUT_MARKER: &str = "\nBinary representation:";
fn solc_path() -> Result<PathBuf> {
let solc_exe = move_command_line_common::env::read_env_var("SOLC_EXE");
if solc_exe.is_empty() {
bail!(
"failed to resolve path to solc (Solidity compiler).
Is the environment variable SOLC_EXE set?
Did you run `./scripts/dev_setup.sh -d`?"
)
}
Ok(PathBuf::from(&solc_exe))
}
fn solc_impl(
source_paths: impl IntoIterator<Item = impl AsRef<OsStr>>,
output_dir: &Path,
) -> Result<BTreeMap<String, Vec<u8>>> {
Command::new(&solc_path()?)
.args(source_paths)
.arg("--bin")
.arg("-o")
.arg(output_dir)
.output()
.map_err(|err| format_err!("failed to call solc (solidity compiler): {:?}", err))?;
let mut compiled_contracts = BTreeMap::new();
for entry in fs::read_dir(output_dir)? {
let entry = entry?;
if entry.file_type()?.is_file() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "bin" {
let data = fs::read(&path)?;
let data = hex::decode(&data)?;
compiled_contracts.insert(
path.file_stem()
.ok_or_else(|| format_err!("failed to extract file name"))?
.to_string_lossy()
.to_string(),
data,
);
}
}
}
}
Ok(compiled_contracts)
}
pub fn solc(
source_paths: impl IntoIterator<Item = impl AsRef<OsStr>>,
) -> Result<BTreeMap<String, Vec<u8>>> {
let temp = tempfile::tempdir()?;
solc_impl(source_paths, temp.path())
}
pub fn solc_yul(source: &str, return_optimized_yul: bool) -> Result<(Vec<u8>, Option<String>)> {
let mut prog = Command::new(&solc_path()?);
prog.arg("--optimize").arg("--strict-assembly").arg("--bin");
if return_optimized_yul {
prog.arg("--ir-optimized");
}
let mut child = prog
.arg("-")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let pipe = child.stdin.as_mut().ok_or(anyhow!("cannot create pipe"))?;
pipe.write_all(source.as_bytes())?;
let out = child.wait_with_output()?;
if !out.status.success() {
return Err(anyhow!(String::from_utf8_lossy(&out.stderr).to_string()));
}
let out_str = String::from_utf8_lossy(&out.stdout).to_string();
let start_of_yul = out_str.find(OPTIMIZED_YUL_MARKER);
let start_of_hex = out_str.find(HEX_OUTPUT_MARKER);
if return_optimized_yul && start_of_yul.is_none() || start_of_hex.is_none() {
return Err(anyhow!(
"Internal error: unexpected output of solc during Yul compilation"
));
}
let yul = if return_optimized_yul {
Some(
(&out_str[(start_of_yul.unwrap() + OPTIMIZED_YUL_MARKER.len())..start_of_hex.unwrap()])
.trim()
.to_string(),
)
} else {
None
};
let bin = hex::decode(&out_str[(start_of_hex.unwrap() + HEX_OUTPUT_MARKER.len())..].trim())?;
Ok((bin, yul))
}