Compare commits

...

4 Commits

Author SHA1 Message Date
Omar Abdulla 4bf22f2d2b Merge remote-tracking branch 'origin/main' into bugfix/finding-contract-abi 2025-07-16 15:21:21 +03:00
Omar Abdulla 5f86ade1e0 Implement ABI fix in the compiler trait impl 2025-07-14 20:31:06 +03:00
Omar Abdulla 43064022e8 Merge remote-tracking branch 'origin/main' into bugfix/finding-contract-abi 2025-07-14 20:25:08 +03:00
Omar Abdulla 4d4398f83e Fix the ABI finding logic 2025-07-13 15:59:23 +03:00
4 changed files with 92 additions and 50 deletions
+1
View File
@@ -49,6 +49,7 @@ pub struct CompilerInput<T: PartialEq + Eq + Hash> {
} }
/// The generic compilation output configuration. /// The generic compilation output configuration.
#[derive(Debug)]
pub struct CompilerOutput<T: PartialEq + Eq + Hash> { pub struct CompilerOutput<T: PartialEq + Eq + Hash> {
/// The solc standard JSON input. /// The solc standard JSON input.
pub input: CompilerInput<T>, pub input: CompilerInput<T>,
+46 -1
View File
@@ -11,6 +11,7 @@ use revive_dt_config::Arguments;
use revive_solc_json_interface::SolcStandardJsonOutput; use revive_solc_json_interface::SolcStandardJsonOutput;
/// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode. /// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode.
#[derive(Debug)]
pub struct Resolc { pub struct Resolc {
/// Path to the `resolc` executable /// Path to the `resolc` executable
resolc_path: PathBuf, resolc_path: PathBuf,
@@ -19,6 +20,7 @@ pub struct Resolc {
impl SolidityCompiler for Resolc { impl SolidityCompiler for Resolc {
type Options = Vec<String>; type Options = Vec<String>;
#[tracing::instrument(level = "debug", ret)]
fn build( fn build(
&self, &self,
input: CompilerInput<Self::Options>, input: CompilerInput<Self::Options>,
@@ -69,7 +71,8 @@ impl SolidityCompiler for Resolc {
}); });
} }
let parsed = serde_json::from_slice::<SolcStandardJsonOutput>(&stdout).map_err(|e| { let mut parsed =
serde_json::from_slice::<SolcStandardJsonOutput>(&stdout).map_err(|e| {
anyhow::anyhow!( anyhow::anyhow!(
"failed to parse resolc JSON output: {e}\nstderr: {}", "failed to parse resolc JSON output: {e}\nstderr: {}",
String::from_utf8_lossy(&stderr) String::from_utf8_lossy(&stderr)
@@ -85,6 +88,48 @@ impl SolidityCompiler for Resolc {
} }
} }
// We need to do some post processing on the output to make it in the same format that solc
// outputs. More specifically, for each contract, the `.metadata` field should be replaced
// with the `.metadata.solc_metadata` field which contains the ABI and other information
// about the compiled contracts. We do this because we do not want any downstream logic to
// need to differentiate between which compiler is being used when extracting the ABI of the
// contracts.
if let Some(ref mut contracts) = parsed.contracts {
for (contract_path, contracts_map) in contracts.iter_mut() {
for (contract_name, contract_info) in contracts_map.iter_mut() {
let Some(metadata) = contract_info.metadata.take() else {
continue;
};
// Get the `solc_metadata` in the metadata of the contract.
let Some(solc_metadata) = metadata
.get("solc_metadata")
.and_then(|metadata| metadata.as_str())
else {
tracing::error!(
contract_path,
contract_name,
metadata = serde_json::to_string(&metadata).unwrap(),
"Encountered a contract compiled with resolc that has no solc_metadata"
);
anyhow::bail!(
"Contract {} compiled with resolc that has no solc_metadata",
contract_name
);
};
// Replace the original metadata with the new solc_metadata.
contract_info.metadata =
Some(serde_json::Value::String(solc_metadata.to_string()));
}
}
}
tracing::debug!(
output = %serde_json::to_string(&parsed).unwrap(),
"Compiled successfully"
);
Ok(CompilerOutput { Ok(CompilerOutput {
input, input,
output: parsed, output: parsed,
+7
View File
@@ -11,6 +11,7 @@ use revive_dt_config::Arguments;
use revive_dt_solc_binaries::download_solc; use revive_dt_solc_binaries::download_solc;
use revive_solc_json_interface::SolcStandardJsonOutput; use revive_solc_json_interface::SolcStandardJsonOutput;
#[derive(Debug)]
pub struct Solc { pub struct Solc {
solc_path: PathBuf, solc_path: PathBuf,
} }
@@ -18,6 +19,7 @@ pub struct Solc {
impl SolidityCompiler for Solc { impl SolidityCompiler for Solc {
type Options = (); type Options = ();
#[tracing::instrument(level = "debug", ret)]
fn build( fn build(
&self, &self,
input: CompilerInput<Self::Options>, input: CompilerInput<Self::Options>,
@@ -75,6 +77,11 @@ impl SolidityCompiler for Solc {
} }
} }
tracing::debug!(
output = %String::from_utf8_lossy(&output.stdout).to_string(),
"Compiled successfully"
);
Ok(CompilerOutput { Ok(CompilerOutput {
input, input,
output: parsed, output: parsed,
+27 -38
View File
@@ -282,51 +282,40 @@ where
std::any::type_name::<T>() std::any::type_name::<T>()
); );
if let Some(Value::String(metadata_json_str)) = &contract.metadata { let Some(Value::String(metadata)) = &contract.metadata else {
tracing::trace!( tracing::error!(?contract, "Contract does not have a metadata field");
"metadata found for contract {contract_name}, {metadata_json_str}" anyhow::bail!("Contract does not have a metadata field: {contract:?}");
); };
match serde_json::from_str::<serde_json::Value>(metadata_json_str) { // Deserialize the solc metadata into a JSON object so we can get the ABI of the
Ok(metadata_json) => { // contracts. If we fail to perform the deserialization then we return an error
if let Some(abi_value) = // as there's no other way to handle this.
metadata_json.get("output").and_then(|o| o.get("abi")) let Ok(metadata) = serde_json::from_str::<Value>(metadata) else {
{ tracing::error!(%metadata, "Failed to parse solc metadata into a structured value");
match serde_json::from_value::<JsonAbi>(abi_value.clone()) {
Ok(parsed_abi) => {
tracing::trace!(
"ABI found in metadata for contract {}",
&contract_name
);
self.deployed_abis
.insert(contract_name.clone(), parsed_abi);
}
Err(err) => {
anyhow::bail!( anyhow::bail!(
"Failed to parse ABI from metadata for contract {}: {}", "Failed to parse solc metadata into a structured value {metadata}"
contract_name,
err
); );
} };
}
} else { // Accessing the ABI on the solc metadata and erroring if the accessing failed
let Some(abi) = metadata.get("output").and_then(|value| value.get("abi"))
else {
tracing::error!(%metadata, "Failed to access the .output.abi field of the solc metadata");
anyhow::bail!( anyhow::bail!(
"No ABI found in metadata for contract {}", "Failed to access the .output.abi field of the solc metadata {metadata}"
contract_name
); );
} };
}
Err(err) => { // Deserialize the ABI object that we got from the unstructured JSON into a
// structured ABI object and error out if we fail.
let Ok(abi) = serde_json::from_value::<JsonAbi>(abi.clone()) else {
tracing::error!(%metadata, "Failed to deserialize ABI into a structured format");
anyhow::bail!( anyhow::bail!(
"Failed to parse metadata JSON string for contract {}: {}", "Failed to deserialize ABI into a structured format {metadata}"
contract_name,
err
); );
} };
}
} else { self.deployed_abis.insert(contract_name.clone(), abi);
anyhow::bail!("No metadata found for contract {}", contract_name);
}
} }
} }
} }