diff --git a/Cargo.lock b/Cargo.lock
index 05cf20bb599c..230e2ed58b90 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3444,9 +3444,9 @@ dependencies = [
 
 [[package]]
 name = "foundry-block-explorers"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "673c42208fee48238ef6833cf55295c9e9b5546383caf426da72d849fb43dc24"
+checksum = "35344cf275788b0450c4b36d452b812d1122d622bafb29887ce244b8099a86ad"
 dependencies = [
  "alloy-chains",
  "alloy-json-abi",
@@ -3597,9 +3597,9 @@ dependencies = [
 
 [[package]]
 name = "foundry-compilers"
-version = "0.6.2"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a92aa3e4d0aa91fda44c1840c68d634fc126bdd06099516eb2b81035e5e6d0"
+checksum = "81f9b10619d07d765a0336b1990ffcb1bb7b806a59b4d2e65cb78f5d77f373c5"
 dependencies = [
  "alloy-json-abi",
  "alloy-primitives",
diff --git a/Cargo.toml b/Cargo.toml
index 2418bdf081ca..65532c07434c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -152,8 +152,8 @@ foundry-wallets = { path = "crates/wallets" }
 foundry-linking = { path = "crates/linking" }
 
 # solc & compilation utilities
-foundry-block-explorers = { version = "0.3.0", default-features = false }
-foundry-compilers = { version = "0.6.2", default-features = false }
+foundry-block-explorers = { version = "0.4.0", default-features = false }
+foundry-compilers = { version = "0.7.0", default-features = false }
 
 ## revm
 # no default features to avoid c-kzg
diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs
index 4ab4b170d9d6..d26b362a9adb 100644
--- a/crates/common/src/compile.rs
+++ b/crates/common/src/compile.rs
@@ -6,11 +6,10 @@ use eyre::{Context, Result};
 use foundry_block_explorers::contract::Metadata;
 use foundry_compilers::{
     artifacts::{BytecodeObject, ContractBytecodeSome, Libraries, Source},
-    compilers::{solc::SolcCompiler, CompilationError, Compiler},
+    compilers::{multi::MultiCompilerLanguage, solc::SolcCompiler, Compiler},
     remappings::Remapping,
     report::{BasicStdoutReporter, NoReporter, Report},
-    Artifact, ArtifactId, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc,
-    SolcConfig,
+    Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc, SolcConfig,
 };
 use foundry_linking::Linker;
 use num_format::{Locale, ToFormattedString};
@@ -20,6 +19,7 @@ use std::{
     fmt::Display,
     io::IsTerminal,
     path::{Path, PathBuf},
+    sync::Arc,
     time::Instant,
 };
 
@@ -122,10 +122,7 @@ impl ProjectCompiler {
     }
 
     /// Compiles the project.
-    pub fn compile<C: Compiler>(
-        mut self,
-        project: &Project<C>,
-    ) -> Result<ProjectCompileOutput<C::CompilationError>> {
+    pub fn compile<C: Compiler>(mut self, project: &Project<C>) -> Result<ProjectCompileOutput<C>> {
         // TODO: Avoid process::exit
         if !project.paths.has_input_files() && self.files.is_empty() {
             println!("Nothing to compile");
@@ -159,9 +156,9 @@ impl ProjectCompiler {
     /// ProjectCompiler::new().compile_with(|| Ok(prj.compile()?)).unwrap();
     /// ```
     #[instrument(target = "forge::compile", skip_all)]
-    fn compile_with<E: CompilationError, F>(self, f: F) -> Result<ProjectCompileOutput<E>>
+    fn compile_with<C: Compiler, F>(self, f: F) -> Result<ProjectCompileOutput<C>>
     where
-        F: FnOnce() -> Result<ProjectCompileOutput<E>>,
+        F: FnOnce() -> Result<ProjectCompileOutput<C>>,
     {
         let quiet = self.quiet.unwrap_or(false);
         let bail = self.bail.unwrap_or(true);
@@ -209,7 +206,7 @@ impl ProjectCompiler {
     }
 
     /// If configured, this will print sizes or names
-    fn handle_output<E>(&self, output: &ProjectCompileOutput<E>) {
+    fn handle_output<C: Compiler>(&self, output: &ProjectCompileOutput<C>) {
         let print_names = self.print_names.unwrap_or(false);
         let print_sizes = self.print_sizes.unwrap_or(false);
 
@@ -274,90 +271,121 @@ impl ProjectCompiler {
     }
 }
 
-/// Contract source code and bytecode.
+#[derive(Clone, Debug)]
+pub struct SourceData {
+    pub source: Arc<String>,
+    pub language: MultiCompilerLanguage,
+}
+
+#[derive(Clone, Debug)]
+pub struct ArtifactData {
+    pub bytecode: ContractBytecodeSome,
+    pub build_id: String,
+    pub file_id: u32,
+}
+
+/// Contract source code and bytecode data used for debugger.
 #[derive(Clone, Debug, Default)]
 pub struct ContractSources {
-    /// Map over artifacts' contract names -> vector of file IDs
-    pub ids_by_name: HashMap<String, Vec<u32>>,
-    /// Map over file_id -> source code
-    pub sources_by_id: FxHashMap<u32, String>,
-    /// Map over file_id -> contract name -> bytecode
-    pub artifacts_by_id: FxHashMap<u32, HashMap<String, ContractBytecodeSome>>,
+    /// Map over build_id -> file_id -> (source code, language)
+    pub sources_by_id: HashMap<String, FxHashMap<u32, SourceData>>,
+    /// Map over contract name -> Vec<(bytecode, build_id, file_id)>
+    pub artifacts_by_name: HashMap<String, Vec<ArtifactData>>,
 }
 
 impl ContractSources {
     /// Collects the contract sources and artifacts from the project compile output.
     pub fn from_project_output(
         output: &ProjectCompileOutput,
-        root: &Path,
-        libraries: &Libraries,
+        link_data: Option<(&Path, &Libraries)>,
     ) -> Result<Self> {
-        let linker = Linker::new(root, output.artifact_ids().collect());
-
         let mut sources = Self::default();
+
+        sources.insert(output, link_data)?;
+
+        Ok(sources)
+    }
+
+    pub fn insert<C: Compiler>(
+        &mut self,
+        output: &ProjectCompileOutput<C>,
+        link_data: Option<(&Path, &Libraries)>,
+    ) -> Result<()>
+    where
+        C::Language: Into<MultiCompilerLanguage>,
+    {
+        let link_data = link_data.map(|(root, libraries)| {
+            let linker = Linker::new(root, output.artifact_ids().collect());
+            (linker, libraries)
+        });
+
         for (id, artifact) in output.artifact_ids() {
             if let Some(file_id) = artifact.id {
-                let abs_path = root.join(&id.source);
-                let source_code = std::fs::read_to_string(abs_path).wrap_err_with(|| {
-                    format!("failed to read artifact source file for `{}`", id.identifier())
-                })?;
-                let linked = linker.link(&id, libraries)?;
-                let contract = compact_to_contract(linked.into_contract_bytecode())?;
-                sources.insert(&id, file_id, source_code, contract);
+                let artifact = if let Some((linker, libraries)) = link_data.as_ref() {
+                    linker.link(&id, libraries)?.into_contract_bytecode()
+                } else {
+                    artifact.clone().into_contract_bytecode()
+                };
+                let bytecode = compact_to_contract(artifact.clone().into_contract_bytecode())?;
+
+                self.artifacts_by_name.entry(id.name.clone()).or_default().push(ArtifactData {
+                    bytecode,
+                    build_id: id.build_id.clone(),
+                    file_id,
+                });
             } else {
                 warn!(id = id.identifier(), "source not found");
             }
         }
-        Ok(sources)
-    }
 
-    /// Inserts a contract into the sources.
-    pub fn insert(
-        &mut self,
-        artifact_id: &ArtifactId,
-        file_id: u32,
-        source: String,
-        bytecode: ContractBytecodeSome,
-    ) {
-        self.ids_by_name.entry(artifact_id.name.clone()).or_default().push(file_id);
-        self.sources_by_id.insert(file_id, source);
-        self.artifacts_by_id.entry(file_id).or_default().insert(artifact_id.name.clone(), bytecode);
-    }
+        // Not all source files produce artifacts, so we are populating sources by using build
+        // infos.
+        let mut files: BTreeMap<PathBuf, Arc<String>> = BTreeMap::new();
+        for (build_id, build) in output.builds() {
+            for (source_id, path) in &build.source_id_to_path {
+                let source_code = if let Some(source) = files.get(path) {
+                    source.clone()
+                } else {
+                    let source = Source::read(path).wrap_err_with(|| {
+                        format!("failed to read artifact source file for `{}`", path.display())
+                    })?;
+                    files.insert(path.clone(), source.content.clone());
+                    source.content
+                };
+
+                self.sources_by_id.entry(build_id.clone()).or_default().insert(
+                    *source_id,
+                    SourceData { source: source_code, language: build.language.into() },
+                );
+            }
+        }
 
-    /// Returns the source for a contract by file ID.
-    pub fn get(&self, id: u32) -> Option<&String> {
-        self.sources_by_id.get(&id)
+        Ok(())
     }
 
     /// Returns all sources for a contract by name.
-    pub fn get_sources<'a>(
-        &'a self,
-        name: &'a str,
-    ) -> Option<impl Iterator<Item = (u32, &'_ str, &'_ ContractBytecodeSome)>> {
-        self.ids_by_name.get(name).map(|ids| {
-            ids.iter().filter_map(|id| {
-                Some((
-                    *id,
-                    self.sources_by_id.get(id)?.as_ref(),
-                    self.artifacts_by_id.get(id)?.get(name)?,
-                ))
+    pub fn get_sources(
+        &self,
+        name: &str,
+    ) -> Option<impl Iterator<Item = (&ArtifactData, &SourceData)>> {
+        self.artifacts_by_name.get(name).map(|artifacts| {
+            artifacts.iter().filter_map(|artifact| {
+                let source =
+                    self.sources_by_id.get(artifact.build_id.as_str())?.get(&artifact.file_id)?;
+                Some((artifact, source))
             })
         })
     }
 
-    /// Returns all (name, source, bytecode) sets.
-    pub fn entries(&self) -> impl Iterator<Item = (&str, &str, &ContractBytecodeSome)> {
-        self.artifacts_by_id
-            .iter()
-            .filter_map(|(id, artifacts)| {
-                let source = self.sources_by_id.get(id)?;
-                Some(
-                    artifacts
-                        .iter()
-                        .map(move |(name, bytecode)| (name.as_ref(), source.as_ref(), bytecode)),
-                )
+    /// Returns all (name, bytecode, source) sets.
+    pub fn entries(&self) -> impl Iterator<Item = (&str, &ArtifactData, &SourceData)> {
+        self.artifacts_by_name.iter().flat_map(|(name, artifacts)| {
+            artifacts.iter().filter_map(|artifact| {
+                let source =
+                    self.sources_by_id.get(artifact.build_id.as_str())?.get(&artifact.file_id)?;
+                Some((name.as_str(), artifact, source))
             })
-            .flatten()
+        })
     }
 }
 
@@ -464,7 +492,7 @@ pub fn compile_target<C: Compiler>(
     target_path: &Path,
     project: &Project<C>,
     quiet: bool,
-) -> Result<ProjectCompileOutput<C::CompilationError>> {
+) -> Result<ProjectCompileOutput<C>> {
     ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project)
 }
 
@@ -472,7 +500,7 @@ pub fn compile_target<C: Compiler>(
 /// Returns the artifact_id, the file_id, and the bytecode
 pub async fn compile_from_source(
     metadata: &Metadata,
-) -> Result<(ArtifactId, u32, ContractBytecodeSome)> {
+) -> Result<ProjectCompileOutput<SolcCompiler>> {
     let root = tempfile::tempdir()?;
     let root_path = root.path();
     let project = etherscan_project(metadata, root_path)?;
@@ -483,23 +511,9 @@ pub async fn compile_from_source(
         eyre::bail!("{project_output}")
     }
 
-    let (artifact_id, file_id, contract) = project_output
-        .into_artifacts()
-        .find(|(artifact_id, _)| artifact_id.name == metadata.contract_name)
-        .map(|(aid, art)| {
-            (aid, art.source_file().expect("no source file").id, art.into_contract_bytecode())
-        })
-        .ok_or_else(|| {
-            eyre::eyre!(
-                "Unable to find bytecode in compiled output for contract: {}",
-                metadata.contract_name
-            )
-        })?;
-    let bytecode = compact_to_contract(contract)?;
-
     root.close()?;
 
-    Ok((artifact_id, file_id, bytecode))
+    Ok(project_output)
 }
 
 /// Creates a [Project] from an Etherscan source.
diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs
index 8e55687a0aec..2909ef803ed6 100644
--- a/crates/debugger/src/tui/draw.rs
+++ b/crates/debugger/src/tui/draw.rs
@@ -3,7 +3,7 @@
 use super::context::{BufferKind, DebuggerContext};
 use crate::op::OpcodeParam;
 use alloy_primitives::U256;
-use foundry_compilers::sourcemap::SourceElement;
+use foundry_compilers::{compilers::multi::MultiCompilerLanguage, sourcemap::SourceElement};
 use ratatui::{
     layout::{Alignment, Constraint, Direction, Layout, Rect},
     style::{Color, Modifier, Style},
@@ -344,32 +344,43 @@ impl DebuggerContext<'_> {
         let is_create = matches!(self.call_kind(), CallKind::Create | CallKind::Create2);
         let pc = self.current_step().pc;
         let Some((source_element, source_code)) =
-            files_source_code.find_map(|(file_id, source_code, contract_source)| {
+            files_source_code.find_map(|(artifact, source)| {
                 let bytecode = if is_create {
-                    &contract_source.bytecode
+                    &artifact.bytecode.bytecode
                 } else {
-                    contract_source.deployed_bytecode.bytecode.as_ref()?
+                    artifact.bytecode.deployed_bytecode.bytecode.as_ref()?
                 };
-                let source_map = bytecode.source_map()?.ok()?;
+                let source_map = bytecode.source_map()?.expect("failed to parse");
 
                 let pc_ic_map = if is_create { create_map } else { rt_map };
                 let ic = pc_ic_map.get(pc)?;
-                let source_element = source_map.get(ic)?;
+
+                // Solc indexes source maps by instruction counter, but Vyper indexes by program
+                // counter.
+                let source_element = if matches!(source.language, MultiCompilerLanguage::Solc(_)) {
+                    source_map.get(ic)?
+                } else {
+                    source_map.get(pc)?
+                };
                 // if the source element has an index, find the sourcemap for that index
-                source_element
+                let res = source_element
                     .index()
                     // if index matches current file_id, return current source code
                     .and_then(|index| {
-                        (index == file_id).then(|| (source_element.clone(), source_code))
+                        (index == artifact.file_id)
+                            .then(|| (source_element.clone(), source.source.as_str()))
                     })
                     .or_else(|| {
                         // otherwise find the source code for the element's index
                         self.debugger
                             .contracts_sources
                             .sources_by_id
+                            .get(&artifact.build_id)?
                             .get(&source_element.index()?)
-                            .map(|source_code| (source_element.clone(), source_code.as_ref()))
-                    })
+                            .map(|source| (source_element.clone(), source.source.as_str()))
+                    });
+
+                res
             })
         else {
             return Err(format!("No source map for contract {contract_name}"));
diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs
index d57c6e7a26ee..c810440e5b13 100644
--- a/crates/debugger/src/tui/mod.rs
+++ b/crates/debugger/src/tui/mod.rs
@@ -66,12 +66,12 @@ impl Debugger {
     ) -> Self {
         let pc_ic_maps = contracts_sources
             .entries()
-            .filter_map(|(contract_name, _, contract)| {
+            .filter_map(|(name, artifact, _)| {
                 Some((
-                    contract_name.to_owned(),
+                    name.to_owned(),
                     (
-                        PcIcMap::new(contract.bytecode.bytes()?),
-                        PcIcMap::new(contract.deployed_bytecode.bytes()?),
+                        PcIcMap::new(artifact.bytecode.bytecode.bytes()?),
+                        PcIcMap::new(artifact.bytecode.deployed_bytecode.bytes()?),
                     ),
                 ))
             })
diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs
index 977b69a42340..794b3a9edd11 100644
--- a/crates/evm/traces/src/identifier/etherscan.rs
+++ b/crates/evm/traces/src/identifier/etherscan.rs
@@ -59,14 +59,11 @@ impl EtherscanIdentifier {
     /// Etherscan and compiles them locally, for usage in the debugger.
     pub async fn get_compiled_contracts(&self) -> eyre::Result<ContractSources> {
         // TODO: Add caching so we dont double-fetch contracts.
-        let contracts_iter = self
+        let outputs_fut = self
             .contracts
             .iter()
             // filter out vyper files
-            .filter(|(_, metadata)| !metadata.is_vyper());
-
-        let outputs_fut = contracts_iter
-            .clone()
+            .filter(|(_, metadata)| !metadata.is_vyper())
             .map(|(address, metadata)| {
                 println!("Compiling: {} {address}", metadata.contract_name);
                 let err_msg =
@@ -76,15 +73,13 @@ impl EtherscanIdentifier {
             .collect::<Vec<_>>();
 
         // poll all the futures concurrently
-        let artifacts = join_all(outputs_fut).await;
+        let outputs = join_all(outputs_fut).await;
 
         let mut sources: ContractSources = Default::default();
 
         // construct the map
-        for (results, (_, metadata)) in artifacts.into_iter().zip(contracts_iter) {
-            // get the inner type
-            let (artifact_id, file_id, bytecode) = results?;
-            sources.insert(&artifact_id, file_id, metadata.source_code(), bytecode);
+        for output in outputs {
+            sources.insert(&output?, None)?;
         }
 
         Ok(sources)
diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs
index dcf76fc2f424..8283ca7d81cc 100644
--- a/crates/forge/bin/cmd/bind.rs
+++ b/crates/forge/bin/cmd/bind.rs
@@ -168,7 +168,7 @@ impl BindArgs {
         Ok(json_files(artifacts)
             .filter_map(|path| {
                 // Ignore the build info JSON.
-                if path.to_str()?.contains("/build-info/") {
+                if path.to_str()?.contains("build-info") {
                     return None;
                 }
 
diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs
index d2de7ef232b9..c27e2053d6f1 100644
--- a/crates/forge/bin/cmd/test/mod.rs
+++ b/crates/forge/bin/cmd/test/mod.rs
@@ -337,8 +337,7 @@ impl TestArgs {
 
             let sources = ContractSources::from_project_output(
                 output_clone.as_ref().unwrap(),
-                project.root(),
-                &libraries,
+                Some((project.root(), &libraries)),
             )?;
 
             // Run the debugger.
diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs
index 1e2be6c7cb84..1c1206fa06a4 100644
--- a/crates/forge/src/multi_runner.rs
+++ b/crates/forge/src/multi_runner.rs
@@ -8,7 +8,9 @@ use alloy_json_abi::{Function, JsonAbi};
 use alloy_primitives::{Address, Bytes, U256};
 use eyre::Result;
 use foundry_common::{get_contract_name, ContractsByArtifact, TestFunctionExt};
-use foundry_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput};
+use foundry_compilers::{
+    artifacts::Libraries, compilers::Compiler, Artifact, ArtifactId, ProjectCompileOutput,
+};
 use foundry_config::Config;
 use foundry_evm::{
     backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork,
@@ -356,10 +358,10 @@ impl MultiContractRunnerBuilder {
 
     /// Given an EVM, proceeds to return a runner which is able to execute all tests
     /// against that evm
-    pub fn build<E>(
+    pub fn build<C: Compiler>(
         self,
         root: &Path,
-        output: ProjectCompileOutput<E>,
+        output: ProjectCompileOutput<C>,
         env: revm::primitives::Env,
         evm_opts: EvmOpts,
     ) -> Result<MultiContractRunner> {
diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs
index e2dca7e0aaa2..93b313742678 100644
--- a/crates/forge/tests/cli/build.rs
+++ b/crates/forge/tests/cli/build.rs
@@ -1,7 +1,8 @@
+use foundry_common::fs::read_json_file;
 use foundry_config::Config;
-use foundry_test_utils::{forgetest, util::OutputExt};
+use foundry_test_utils::forgetest;
 use globset::Glob;
-use std::path::PathBuf;
+use std::{collections::BTreeMap, path::PathBuf};
 
 // tests that json is printed when --json is passed
 forgetest!(compile_json, |prj, cmd| {
@@ -21,10 +22,17 @@ contract Dummy {
     // set up command
     cmd.args(["compile", "--format-json"]);
 
-    // run command and assert
-    cmd.unchecked_output().stdout_matches_path(
-        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/compile_json.stdout"),
-    );
+    // Exclude build_infos from output as IDs depend on root dir and are not deterministic.
+    let mut output: BTreeMap<String, serde_json::Value> =
+        serde_json::from_str(&cmd.stdout_lossy()).unwrap();
+    output.remove("build_infos");
+
+    let expected: BTreeMap<String, serde_json::Value> = read_json_file(
+        &PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/compile_json.stdout"),
+    )
+    .unwrap();
+
+    similar_asserts::assert_eq!(output, expected);
 });
 
 // tests build output is as expected
diff --git a/crates/forge/tests/fixtures/compile_json.stdout b/crates/forge/tests/fixtures/compile_json.stdout
index f3eb33ea0d8c..eff78e60c8d7 100644
--- a/crates/forge/tests/fixtures/compile_json.stdout
+++ b/crates/forge/tests/fixtures/compile_json.stdout
@@ -1,4 +1,5 @@
 {
+  "contracts": {},
   "errors": [
     {
       "sourceLocation": {
@@ -11,10 +12,8 @@
       "severity": "error",
       "errorCode": "7576",
       "message": "Undeclared identifier. Did you mean \"newNumber\"?",
-      "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n --> src/dummy.sol:7:18:\n  |\n7 |         number = newnumber; // error here\n  |                  ^^^^^^^^^\n\n"
+      "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n --> src/jsonError.sol:7:18:\n  |\n7 |         number = newnumber; // error here\n  |                  ^^^^^^^^^\n\n"
     }
   ],
-  "sources": {},
-  "contracts": {},
-  "build_infos": {}
-}
+  "sources": {}
+}
\ No newline at end of file
diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs
index 342096fda3d1..f1a18d45fa8b 100644
--- a/crates/script/src/build.rs
+++ b/crates/script/src/build.rs
@@ -136,8 +136,7 @@ impl LinkedBuildData {
     ) -> Result<Self> {
         let sources = ContractSources::from_project_output(
             &build_data.output,
-            &build_data.project_root,
-            &libraries,
+            Some((&build_data.project_root, &libraries)),
         )?;
 
         let known_contracts =
diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs
index e1a681db62f8..6d2d5233a3c6 100644
--- a/crates/test-utils/src/util.rs
+++ b/crates/test-utils/src/util.rs
@@ -1083,7 +1083,7 @@ static IGNORE_IN_FIXTURES: Lazy<Regex> = Lazy::new(|| {
     Regex::new(&format!("({})", re.join("|"))).unwrap()
 });
 
-fn normalize_output(s: &str) -> String {
+pub fn normalize_output(s: &str) -> String {
     let s = s.replace("\r\n", "\n").replace('\\', "/");
     IGNORE_IN_FIXTURES.replace_all(&s, "").into_owned()
 }
diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs
index fa888f92295a..ac586b48cd13 100644
--- a/crates/verify/src/etherscan/flatten.rs
+++ b/crates/verify/src/etherscan/flatten.rs
@@ -4,6 +4,7 @@ use eyre::{Context, Result};
 use foundry_block_explorers::verify::CodeFormat;
 use foundry_compilers::{
     artifacts::{BytecodeHash, Source},
+    buildinfo::RawBuildInfo,
     compilers::{
         solc::{SolcCompiler, SolcLanguage, SolcVersionedInput},
         Compiler, CompilerInput,
@@ -87,8 +88,8 @@ impl EtherscanFlattenedSource {
 
         let out = SolcCompiler::Specific(solc).compile(&input)?;
         if out.errors.iter().any(|e| e.is_error()) {
-            let mut o = AggregatedCompilerOutput::default();
-            o.extend(version, out);
+            let mut o = AggregatedCompilerOutput::<SolcCompiler>::default();
+            o.extend(version.clone(), RawBuildInfo::new(&input, &out, false)?, out);
             let diags = o.diagnostics(&[], &[], Default::default());
 
             eyre::bail!(