Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ use foundry_common::{
TransactionReceiptWithRevertReason,
abi::{coerce_value, encode_function_args, get_event, get_func},
compile::etherscan_project,
flatten,
fmt::*,
fs, get_pretty_tx_receipt_attr, shell,
};
use foundry_compilers::flatten::Flattener;
use foundry_config::Chain;
use foundry_evm_core::ic::decode_instructions;
use futures::{FutureExt, StreamExt, future::Either};
Expand Down Expand Up @@ -2192,7 +2192,7 @@ impl SimpleCast {
let project = etherscan_project(metadata, tmp.path())?;
let target_path = project.find_contract_path(&metadata.contract_name)?;

let flattened = Flattener::new(project, &target_path)?.flatten();
let flattened = flatten(project, &target_path)?;

if let Some(path) = output_path {
fs::create_dir_all(path.parent().unwrap())?;
Expand Down
25 changes: 23 additions & 2 deletions crates/common/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
//! Uncategorised utilities.

use alloy_primitives::{B256, Bytes, U256, hex, keccak256};
use foundry_compilers::artifacts::BytecodeObject;
use foundry_compilers::{
Project,
artifacts::{BytecodeObject, SolcLanguage},
error::SolcError,
flatten::{Flattener, FlattenerError},
};
use regex::Regex;
use std::sync::LazyLock;
use std::{path::Path, sync::LazyLock};

static BYTECODE_PLACEHOLDER_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"__\$.{34}\$__").expect("invalid regex"));
Expand Down Expand Up @@ -84,3 +89,19 @@ pub fn strip_bytecode_placeholders(bytecode: &BytecodeObject) -> Option<Bytes> {
}
}
}

/// Flattens the given target of the project. Falls back to the old flattening implementation
/// if the target cannot be compiled successfully. This would be the case if the target has invalid
/// syntax. (e.g. Solang)
pub fn flatten(project: Project, target_path: &Path) -> eyre::Result<String> {
let flattened = match Flattener::new(project.clone(), target_path) {
Ok(flattener) => Ok(flattener.flatten()),
Err(FlattenerError::Compilation(_)) => {
project.paths.with_language::<SolcLanguage>().flatten(target_path)
}
Err(FlattenerError::Other(err)) => Err(err),
}
.map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?;

Ok(flattened)
}
24 changes: 2 additions & 22 deletions crates/forge/src/cmd/flatten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ use foundry_cli::{
opts::{BuildOpts, ProjectPathOpts},
utils::LoadConfig,
};
use foundry_common::{compile::with_compilation_reporter, fs};
use foundry_compilers::{
compilers::solc::SolcLanguage,
error::SolcError,
flatten::{Flattener, FlattenerError},
};
use foundry_common::{flatten, fs};
use std::path::PathBuf;

/// CLI arguments for `forge flatten`.
Expand Down Expand Up @@ -44,22 +39,7 @@ impl FlattenArgs {
let project = config.ephemeral_project()?;

let target_path = dunce::canonicalize(target_path)?;

let flattener = with_compilation_reporter(true, Some(project.root().to_path_buf()), || {
Flattener::new(project.clone(), &target_path)
});

let flattened = match flattener {
Ok(flattener) => Ok(flattener.flatten()),
Err(FlattenerError::Compilation(_)) => {
// Fallback to the old flattening implementation if we couldn't compile the target
// successfully. This would be the case if the target has invalid
// syntax. (e.g. Solang)
project.paths.with_language::<SolcLanguage>().flatten(&target_path)
}
Err(FlattenerError::Other(err)) => Err(err),
}
.map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?;
let flattened = flatten(project, &target_path)?;

match output {
Some(output) => {
Expand Down
22 changes: 10 additions & 12 deletions crates/verify/src/etherscan/flatten.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::{EtherscanSourceProvider, VerifyArgs};
use crate::provider::VerificationContext;
use eyre::{Context, Result};
use eyre::Result;
use foundry_block_explorers::verify::CodeFormat;
use foundry_common::flatten;
use foundry_compilers::{
AggregatedCompilerOutput,
artifacts::{BytecodeHash, Source, Sources},
Expand Down Expand Up @@ -32,18 +33,15 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource {
bch,
);

let source = context
.project
.paths
.clone()
.with_language::<SolcLanguage>()
.flatten(&context.target_path)
.wrap_err("Failed to flatten contract")?;

let flattened_source = flatten(context.project.clone(), &context.target_path)?;
if !args.force {
// solc dry run of flattened code
self.check_flattened(source.clone(), &context.compiler_version, &context.target_path)
.map_err(|err| {
self.check_flattened(
flattened_source.clone(),
&context.compiler_version,
&context.target_path,
)
.map_err(|err| {
eyre::eyre!(
"Failed to compile the flattened code locally: `{}`\
To skip this solc dry, have a look at the `--force` flag of this command.",
Expand All @@ -52,7 +50,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource {
})?;
}

Ok((source, context.target_name.clone(), CodeFormat::SingleFile))
Ok((flattened_source, context.target_name.clone(), CodeFormat::SingleFile))
}
}

Expand Down
Loading