mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-28 00:28:02 +00:00
Emerge Yul recompiler (#1)
Provide a modified (and incomplete) version of ZKSync zksolc that can compile the most basic contracts
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
//!
|
||||
//! The Solidity contract build.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::combined_json::contract::Contract as CombinedJsonContract;
|
||||
use crate::solc::standard_json::output::contract::Contract as StandardJsonOutputContract;
|
||||
|
||||
///
|
||||
/// The Solidity contract build.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Contract {
|
||||
/// The contract path.
|
||||
pub path: String,
|
||||
/// The auxiliary identifier. Used to identify Yul objects.
|
||||
pub identifier: String,
|
||||
/// The LLVM module build.
|
||||
pub build: era_compiler_llvm_context::EraVMBuild,
|
||||
/// The metadata JSON.
|
||||
pub metadata_json: serde_json::Value,
|
||||
/// The factory dependencies.
|
||||
pub factory_dependencies: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Contract {
|
||||
///
|
||||
/// A shortcut constructor.
|
||||
///
|
||||
pub fn new(
|
||||
path: String,
|
||||
identifier: String,
|
||||
build: era_compiler_llvm_context::EraVMBuild,
|
||||
metadata_json: serde_json::Value,
|
||||
factory_dependencies: HashSet<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
path,
|
||||
identifier,
|
||||
build,
|
||||
metadata_json,
|
||||
factory_dependencies,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Writes the contract text assembly and bytecode to files.
|
||||
///
|
||||
pub fn write_to_directory(
|
||||
self,
|
||||
path: &Path,
|
||||
output_assembly: bool,
|
||||
output_binary: bool,
|
||||
overwrite: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let file_name = Self::short_path(self.path.as_str());
|
||||
|
||||
if output_assembly {
|
||||
let file_name = format!(
|
||||
"{}.{}",
|
||||
file_name,
|
||||
era_compiler_common::EXTENSION_ERAVM_ASSEMBLY
|
||||
);
|
||||
let mut file_path = path.to_owned();
|
||||
file_path.push(file_name);
|
||||
|
||||
if file_path.exists() && !overwrite {
|
||||
eprintln!(
|
||||
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
|
||||
);
|
||||
} else {
|
||||
File::create(&file_path)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("File {:?} creating error: {}", file_path, error)
|
||||
})?
|
||||
.write_all(self.build.assembly_text.as_bytes())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("File {:?} writing error: {}", file_path, error)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
if output_binary {
|
||||
let file_name = format!(
|
||||
"{}.{}",
|
||||
file_name,
|
||||
era_compiler_common::EXTENSION_ERAVM_BINARY
|
||||
);
|
||||
let mut file_path = path.to_owned();
|
||||
file_path.push(file_name);
|
||||
|
||||
if file_path.exists() && !overwrite {
|
||||
eprintln!(
|
||||
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
|
||||
);
|
||||
} else {
|
||||
File::create(&file_path)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("File {:?} creating error: {}", file_path, error)
|
||||
})?
|
||||
.write_all(self.build.bytecode.as_slice())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("File {:?} writing error: {}", file_path, error)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Writes the contract text assembly and bytecode to the combined JSON.
|
||||
///
|
||||
pub fn write_to_combined_json(
|
||||
self,
|
||||
combined_json_contract: &mut CombinedJsonContract,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(metadata) = combined_json_contract.metadata.as_mut() {
|
||||
*metadata = self.metadata_json.to_string();
|
||||
}
|
||||
|
||||
if let Some(asm) = combined_json_contract.asm.as_mut() {
|
||||
*asm = serde_json::Value::String(self.build.assembly_text);
|
||||
}
|
||||
let hexadecimal_bytecode = hex::encode(self.build.bytecode);
|
||||
combined_json_contract.bin = Some(hexadecimal_bytecode);
|
||||
combined_json_contract.bin_runtime = combined_json_contract.bin.clone();
|
||||
|
||||
combined_json_contract.factory_deps = Some(self.build.factory_dependencies);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Writes the contract text assembly and bytecode to the standard JSON.
|
||||
///
|
||||
pub fn write_to_standard_json(
|
||||
self,
|
||||
standard_json_contract: &mut StandardJsonOutputContract,
|
||||
) -> anyhow::Result<()> {
|
||||
standard_json_contract.metadata = Some(self.metadata_json);
|
||||
|
||||
let assembly_text = self.build.assembly_text;
|
||||
let bytecode = hex::encode(self.build.bytecode.as_slice());
|
||||
if let Some(evm) = standard_json_contract.evm.as_mut() {
|
||||
evm.modify(assembly_text, bytecode);
|
||||
}
|
||||
|
||||
standard_json_contract.factory_dependencies = Some(self.build.factory_dependencies);
|
||||
standard_json_contract.hash = Some(self.build.bytecode_hash);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts the full path to a short one.
|
||||
///
|
||||
pub fn short_path(path: &str) -> &str {
|
||||
path.rfind('/')
|
||||
.map(|last_slash| &path[last_slash + 1..])
|
||||
.unwrap_or_else(|| path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//!
|
||||
//! The Solidity project build.
|
||||
//!
|
||||
|
||||
pub mod contract;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::solc::combined_json::CombinedJson;
|
||||
use crate::solc::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
|
||||
use self::contract::Contract;
|
||||
|
||||
///
|
||||
/// The Solidity project build.
|
||||
///
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Build {
|
||||
/// The contract data,
|
||||
pub contracts: BTreeMap<String, Contract>,
|
||||
}
|
||||
|
||||
impl Build {
|
||||
///
|
||||
/// Writes all contracts to the specified directory.
|
||||
///
|
||||
pub fn write_to_directory(
|
||||
self,
|
||||
output_directory: &Path,
|
||||
output_assembly: bool,
|
||||
output_binary: bool,
|
||||
overwrite: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
for (_path, contract) in self.contracts.into_iter() {
|
||||
contract.write_to_directory(
|
||||
output_directory,
|
||||
output_assembly,
|
||||
output_binary,
|
||||
overwrite,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Writes all contracts assembly and bytecode to the combined JSON.
|
||||
///
|
||||
pub fn write_to_combined_json(
|
||||
self,
|
||||
combined_json: &mut CombinedJson,
|
||||
zksolc_version: &semver::Version,
|
||||
) -> anyhow::Result<()> {
|
||||
for (path, contract) in self.contracts.into_iter() {
|
||||
let combined_json_contract = combined_json
|
||||
.contracts
|
||||
.iter_mut()
|
||||
.find_map(|(json_path, contract)| {
|
||||
if path.ends_with(json_path) {
|
||||
Some(contract)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| anyhow::anyhow!("Contract `{}` not found in the project", path))?;
|
||||
|
||||
contract.write_to_combined_json(combined_json_contract)?;
|
||||
}
|
||||
|
||||
combined_json.zk_version = Some(zksolc_version.to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Writes all contracts assembly and bytecode to the standard JSON.
|
||||
///
|
||||
pub fn write_to_standard_json(
|
||||
mut self,
|
||||
standard_json: &mut StandardJsonOutput,
|
||||
solc_version: &SolcVersion,
|
||||
zksolc_version: &semver::Version,
|
||||
) -> anyhow::Result<()> {
|
||||
let contracts = match standard_json.contracts.as_mut() {
|
||||
Some(contracts) => contracts,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
for (path, contracts) in contracts.iter_mut() {
|
||||
for (name, contract) in contracts.iter_mut() {
|
||||
let full_name = format!("{path}:{name}");
|
||||
|
||||
if let Some(contract_data) = self.contracts.remove(full_name.as_str()) {
|
||||
contract_data.write_to_standard_json(contract)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
standard_json.version = Some(solc_version.default.to_string());
|
||||
standard_json.long_version = Some(solc_version.long.to_owned());
|
||||
standard_json.zk_version = Some(zksolc_version.to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user