foundry_compilers/artifact_output/
configurable.rs

1//! A configurable artifacts handler implementation
2//!
3//! Configuring artifacts requires two pieces: the `ConfigurableArtifacts` handler, which contains
4//! the configuration of how to construct the `ConfigurableArtifact` type based on a `Contract`. The
5//! `ConfigurableArtifacts` populates a single `Artifact`, the `ConfigurableArtifact`, by default
6//! with essential entries only, such as `abi`, `bytecode`,..., but may include additional values
7//! based on its `ExtraOutputValues` that maps to various objects in the solc contract output, see
8//! also: [`OutputSelection`](foundry_compilers_artifacts::output_selection::OutputSelection). In
9//! addition to that some output values can also be emitted as standalone files.
10
11use crate::{
12    sources::VersionedSourceFile, Artifact, ArtifactFile, ArtifactOutput, SolcConfig, SolcError,
13    SourceFile,
14};
15use alloy_json_abi::JsonAbi;
16use alloy_primitives::hex;
17use foundry_compilers_artifacts::{
18    bytecode::{CompactBytecode, CompactDeployedBytecode},
19    contract::Contract,
20    output_selection::{
21        BytecodeOutputSelection, ContractOutputSelection, DeployedBytecodeOutputSelection,
22        EvmOutputSelection, EwasmOutputSelection,
23    },
24    BytecodeObject, ConfigurableContractArtifact, Evm, Ewasm, GeneratedSource, LosslessMetadata,
25    Metadata, Settings,
26};
27use foundry_compilers_core::utils;
28use std::{fs, path::Path};
29
30/// An `Artifact` implementation that can be configured to include additional content and emit
31/// additional files
32///
33/// Creates a single json artifact with
34/// ```json
35///  {
36///    "abi": [],
37///    "bytecode": {...},
38///    "deployedBytecode": {...},
39///    "methodIdentifiers": {...},
40///    // additional values
41///  }
42/// ```
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
44pub struct ConfigurableArtifacts {
45    /// A set of additional values to include in the contract's artifact file
46    pub additional_values: ExtraOutputValues,
47
48    /// A set of values that should be written to a separate file
49    pub additional_files: ExtraOutputFiles,
50
51    /// PRIVATE: This structure may grow, As such, constructing this structure should
52    /// _always_ be done using a public constructor or update syntax:
53    ///
54    /// ```
55    /// use foundry_compilers::{ConfigurableArtifacts, ExtraOutputFiles};
56    ///
57    /// let config = ConfigurableArtifacts {
58    ///     additional_files: ExtraOutputFiles { metadata: true, ..Default::default() },
59    ///     ..Default::default()
60    /// };
61    /// ```
62    #[doc(hidden)]
63    pub __non_exhaustive: (),
64}
65
66impl ConfigurableArtifacts {
67    pub fn new(
68        extra_values: impl IntoIterator<Item = ContractOutputSelection>,
69        extra_files: impl IntoIterator<Item = ContractOutputSelection>,
70    ) -> Self {
71        Self {
72            additional_values: ExtraOutputValues::from_output_selection(extra_values),
73            additional_files: ExtraOutputFiles::from_output_selection(extra_files),
74            ..Default::default()
75        }
76    }
77
78    /// Returns the `Settings` this configuration corresponds to
79    pub fn solc_settings(&self) -> Settings {
80        SolcConfig::builder()
81            .additional_outputs(self.output_selection())
82            .ast(self.additional_values.ast)
83            .build()
84    }
85
86    /// Returns the output selection corresponding to this configuration
87    pub fn output_selection(&self) -> Vec<ContractOutputSelection> {
88        let mut selection = ContractOutputSelection::basic();
89
90        let ExtraOutputValues {
91            // handled above
92            ast: _,
93            userdoc,
94            devdoc,
95            method_identifiers,
96            storage_layout,
97            transient_storage_layout,
98            assembly,
99            legacy_assembly,
100            gas_estimates,
101            metadata,
102            ir,
103            ir_optimized,
104            ir_optimized_ast,
105            ewasm,
106            function_debug_data,
107            generated_sources,
108            source_map,
109            opcodes,
110            __non_exhaustive,
111        } = self.additional_values;
112
113        if ir || self.additional_files.ir {
114            selection.push(ContractOutputSelection::Ir);
115        }
116        if ir_optimized || self.additional_files.ir_optimized {
117            selection.push(ContractOutputSelection::IrOptimized);
118        }
119        if metadata || self.additional_files.metadata {
120            selection.push(ContractOutputSelection::Metadata);
121        }
122        if storage_layout {
123            selection.push(ContractOutputSelection::StorageLayout);
124        }
125        if devdoc {
126            selection.push(ContractOutputSelection::DevDoc);
127        }
128        if userdoc {
129            selection.push(ContractOutputSelection::UserDoc);
130        }
131        if gas_estimates {
132            selection.push(EvmOutputSelection::GasEstimates.into());
133        }
134        if assembly || self.additional_files.assembly {
135            selection.push(EvmOutputSelection::Assembly.into());
136        }
137        if legacy_assembly || self.additional_files.legacy_assembly {
138            selection.push(EvmOutputSelection::LegacyAssembly.into());
139        }
140        if ewasm || self.additional_files.ewasm {
141            selection.push(EwasmOutputSelection::All.into());
142        }
143        if function_debug_data {
144            selection.push(BytecodeOutputSelection::FunctionDebugData.into());
145        }
146        if method_identifiers {
147            selection.push(EvmOutputSelection::MethodIdentifiers.into());
148        }
149        if generated_sources {
150            selection.push(
151                EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources).into(),
152            );
153        }
154        if source_map {
155            selection.push(EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap).into());
156        }
157        if ir_optimized_ast {
158            selection.push(ContractOutputSelection::IrOptimizedAst);
159        }
160        if opcodes {
161            selection.push(EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes).into());
162        }
163        if transient_storage_layout {
164            selection.push(ContractOutputSelection::TransientStorageLayout);
165        }
166        selection
167    }
168}
169
170impl ArtifactOutput for ConfigurableArtifacts {
171    type Artifact = ConfigurableContractArtifact;
172    type CompilerContract = Contract;
173
174    /// Writes extra files for compiled artifact based on [Self::additional_files]
175    fn handle_artifacts(
176        &self,
177        contracts: &crate::VersionedContracts<Contract>,
178        artifacts: &crate::Artifacts<Self::Artifact>,
179    ) -> Result<(), SolcError> {
180        for (file, contracts) in contracts.as_ref().iter() {
181            for (name, versioned_contracts) in contracts {
182                for contract in versioned_contracts {
183                    if let Some(artifact) = artifacts.find_artifact(file, name, &contract.version) {
184                        let file = &artifact.file;
185                        utils::create_parent_dir_all(file)?;
186                        self.additional_files.write_extras(&contract.contract, file)?;
187                    }
188                }
189            }
190        }
191        Ok(())
192    }
193
194    fn contract_to_artifact(
195        &self,
196        _file: &Path,
197        _name: &str,
198        contract: Contract,
199        source_file: Option<&SourceFile>,
200    ) -> Self::Artifact {
201        let mut artifact_userdoc = None;
202        let mut artifact_devdoc = None;
203        let mut artifact_raw_metadata = None;
204        let mut artifact_metadata = None;
205        let mut artifact_ir = None;
206        let mut artifact_ir_optimized = None;
207        let mut artifact_ir_optimized_ast = None;
208        let mut artifact_ewasm = None;
209        let mut artifact_bytecode = None;
210        let mut artifact_deployed_bytecode = None;
211        let mut artifact_gas_estimates = None;
212        let mut artifact_function_debug_data = None;
213        let mut artifact_method_identifiers = None;
214        let mut artifact_assembly = None;
215        let mut artifact_legacy_assembly = None;
216        let mut artifact_storage_layout = None;
217        let mut artifact_transient_storage_layout = None;
218        let mut generated_sources = None;
219        let mut opcodes = None;
220
221        let Contract {
222            abi,
223            metadata,
224            userdoc,
225            devdoc,
226            ir,
227            storage_layout,
228            transient_storage_layout,
229            evm,
230            ewasm,
231            ir_optimized,
232            ir_optimized_ast,
233        } = contract;
234
235        if self.additional_values.metadata || self.additional_files.metadata {
236            if let Some(LosslessMetadata { raw_metadata, metadata }) = metadata {
237                artifact_raw_metadata = Some(raw_metadata);
238                artifact_metadata = Some(metadata);
239            }
240        }
241        if self.additional_values.userdoc {
242            artifact_userdoc = Some(userdoc);
243        }
244        if self.additional_values.devdoc {
245            artifact_devdoc = Some(devdoc);
246        }
247        if self.additional_values.ewasm || self.additional_files.ewasm {
248            artifact_ewasm = ewasm;
249        }
250        if self.additional_values.ir || self.additional_files.ir {
251            artifact_ir = ir;
252        }
253        if self.additional_values.ir_optimized || self.additional_files.ir_optimized {
254            artifact_ir_optimized = ir_optimized;
255        }
256        if self.additional_values.ir_optimized_ast {
257            artifact_ir_optimized_ast = ir_optimized_ast;
258        }
259        if self.additional_values.storage_layout {
260            artifact_storage_layout = Some(storage_layout);
261        }
262        if self.additional_values.transient_storage_layout {
263            artifact_transient_storage_layout = Some(transient_storage_layout);
264        }
265
266        if let Some(evm) = evm {
267            let Evm {
268                assembly,
269                mut bytecode,
270                deployed_bytecode,
271                method_identifiers,
272                gas_estimates,
273                legacy_assembly,
274            } = evm;
275
276            if self.additional_values.function_debug_data {
277                artifact_function_debug_data =
278                    bytecode.as_mut().map(|code| std::mem::take(&mut code.function_debug_data));
279            }
280            if self.additional_values.generated_sources {
281                generated_sources =
282                    bytecode.as_mut().map(|code| std::mem::take(&mut code.generated_sources));
283            }
284
285            if self.additional_values.opcodes {
286                opcodes = bytecode.as_mut().and_then(|code| code.opcodes.take())
287            }
288
289            artifact_bytecode = bytecode.map(Into::into);
290            artifact_deployed_bytecode = deployed_bytecode.map(Into::into);
291            artifact_method_identifiers = Some(method_identifiers);
292
293            if self.additional_values.gas_estimates {
294                artifact_gas_estimates = gas_estimates;
295            }
296            if self.additional_values.assembly || self.additional_files.assembly {
297                artifact_assembly = assembly;
298            }
299
300            if self.additional_values.legacy_assembly || self.additional_files.legacy_assembly {
301                artifact_legacy_assembly = legacy_assembly;
302            }
303        }
304
305        ConfigurableContractArtifact {
306            abi,
307            bytecode: artifact_bytecode,
308            deployed_bytecode: artifact_deployed_bytecode,
309            assembly: artifact_assembly,
310            legacy_assembly: artifact_legacy_assembly,
311            opcodes,
312            function_debug_data: artifact_function_debug_data,
313            method_identifiers: artifact_method_identifiers,
314            gas_estimates: artifact_gas_estimates,
315            raw_metadata: artifact_raw_metadata,
316            metadata: artifact_metadata,
317            storage_layout: artifact_storage_layout,
318            transient_storage_layout: artifact_transient_storage_layout,
319            userdoc: artifact_userdoc,
320            devdoc: artifact_devdoc,
321            ir: artifact_ir,
322            ir_optimized: artifact_ir_optimized,
323            ir_optimized_ast: artifact_ir_optimized_ast,
324            ewasm: artifact_ewasm,
325            id: source_file.as_ref().map(|s| s.id),
326            ast: source_file.and_then(|s| s.ast.clone()),
327            generated_sources: generated_sources.unwrap_or_default(),
328        }
329    }
330
331    fn standalone_source_file_to_artifact(
332        &self,
333        _path: &Path,
334        file: &VersionedSourceFile,
335    ) -> Option<Self::Artifact> {
336        file.source_file.ast.clone().map(|ast| ConfigurableContractArtifact {
337            abi: Some(JsonAbi::default()),
338            id: Some(file.source_file.id),
339            ast: Some(ast),
340            bytecode: Some(CompactBytecode::empty()),
341            deployed_bytecode: Some(CompactDeployedBytecode::empty()),
342            ..Default::default()
343        })
344    }
345
346    /// We want to enforce recompilation if artifact is missing data we need for writing extra
347    /// files.
348    fn is_dirty(&self, artifact_file: &ArtifactFile<Self::Artifact>) -> Result<bool, SolcError> {
349        let artifact = &artifact_file.artifact;
350        let ExtraOutputFiles {
351            abi: _,
352            metadata,
353            ir,
354            ir_optimized,
355            ewasm,
356            assembly,
357            legacy_assembly,
358            source_map,
359            generated_sources,
360            bytecode: _,
361            deployed_bytecode: _,
362            __non_exhaustive: _,
363        } = self.additional_files;
364
365        if metadata && artifact.metadata.is_none() {
366            return Ok(true);
367        }
368        if ir && artifact.ir.is_none() {
369            return Ok(true);
370        }
371        if ir_optimized && artifact.ir_optimized.is_none() {
372            return Ok(true);
373        }
374        if ewasm && artifact.ewasm.is_none() {
375            return Ok(true);
376        }
377        if assembly && artifact.assembly.is_none() {
378            return Ok(true);
379        }
380        if assembly && artifact.assembly.is_none() {
381            return Ok(true);
382        }
383        if legacy_assembly && artifact.legacy_assembly.is_none() {
384            return Ok(true);
385        }
386        if source_map && artifact.get_source_map_str().is_none() {
387            return Ok(true);
388        }
389        if generated_sources {
390            // We can't check if generated sources are missing or just empty.
391            return Ok(true);
392        }
393        Ok(false)
394    }
395
396    /// Writes extra files for cached artifacts based on [Self::additional_files].
397    fn handle_cached_artifacts(
398        &self,
399        artifacts: &crate::Artifacts<Self::Artifact>,
400    ) -> Result<(), SolcError> {
401        for artifacts in artifacts.values() {
402            for artifacts in artifacts.values() {
403                for artifact_file in artifacts {
404                    let file = &artifact_file.file;
405                    let artifact = &artifact_file.artifact;
406                    self.additional_files.process_abi(artifact.abi.as_ref(), file)?;
407                    self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?;
408                    self.additional_files
409                        .process_legacy_assembly(artifact.legacy_assembly.clone(), file)?;
410                    self.additional_files
411                        .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?;
412                    self.additional_files.process_deployed_bytecode(
413                        artifact
414                            .deployed_bytecode
415                            .as_ref()
416                            .and_then(|d| d.bytecode.as_ref())
417                            .map(|b| &b.object),
418                        file,
419                    )?;
420                    self.additional_files
421                        .process_generated_sources(Some(&artifact.generated_sources), file)?;
422                    self.additional_files.process_ir(artifact.ir.as_deref(), file)?;
423                    self.additional_files
424                        .process_ir_optimized(artifact.ir_optimized.as_deref(), file)?;
425                    self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?;
426                    self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?;
427                    self.additional_files
428                        .process_source_map(artifact.get_source_map_str().as_deref(), file)?;
429                }
430            }
431        }
432
433        Ok(())
434    }
435}
436
437/// Determines the additional values to include in the contract's artifact file
438#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
439pub struct ExtraOutputValues {
440    pub ast: bool,
441    pub userdoc: bool,
442    pub devdoc: bool,
443    pub method_identifiers: bool,
444    pub storage_layout: bool,
445    pub transient_storage_layout: bool,
446    pub assembly: bool,
447    pub legacy_assembly: bool,
448    pub gas_estimates: bool,
449    pub metadata: bool,
450    pub ir: bool,
451    pub ir_optimized: bool,
452    pub ir_optimized_ast: bool,
453    pub ewasm: bool,
454    pub function_debug_data: bool,
455    pub generated_sources: bool,
456    pub source_map: bool,
457    pub opcodes: bool,
458
459    /// PRIVATE: This structure may grow, As such, constructing this structure should
460    /// _always_ be done using a public constructor or update syntax:
461    ///
462    /// ```
463    /// use foundry_compilers::ExtraOutputValues;
464    ///
465    /// let config = ExtraOutputValues { ir: true, ..Default::default() };
466    /// ```
467    #[doc(hidden)]
468    pub __non_exhaustive: (),
469}
470
471impl ExtraOutputValues {
472    /// Returns an instance where all values are set to `true`
473    pub fn all() -> Self {
474        Self {
475            ast: true,
476            userdoc: true,
477            devdoc: true,
478            method_identifiers: true,
479            storage_layout: true,
480            transient_storage_layout: true,
481            assembly: true,
482            legacy_assembly: true,
483            gas_estimates: true,
484            metadata: true,
485            ir: true,
486            ir_optimized: true,
487            ir_optimized_ast: true,
488            ewasm: true,
489            function_debug_data: true,
490            generated_sources: true,
491            source_map: true,
492            opcodes: true,
493            __non_exhaustive: (),
494        }
495    }
496
497    /// Sets the values based on a set of `ContractOutputSelection`
498    pub fn from_output_selection(
499        settings: impl IntoIterator<Item = ContractOutputSelection>,
500    ) -> Self {
501        let mut config = Self::default();
502        for value in settings.into_iter() {
503            match value {
504                ContractOutputSelection::DevDoc => {
505                    config.devdoc = true;
506                }
507                ContractOutputSelection::UserDoc => {
508                    config.userdoc = true;
509                }
510                ContractOutputSelection::Metadata => {
511                    config.metadata = true;
512                }
513                ContractOutputSelection::Ir => {
514                    config.ir = true;
515                }
516                ContractOutputSelection::IrOptimized => {
517                    config.ir_optimized = true;
518                }
519                ContractOutputSelection::StorageLayout => {
520                    config.storage_layout = true;
521                }
522                ContractOutputSelection::Evm(evm) => match evm {
523                    EvmOutputSelection::All => {
524                        config.assembly = true;
525                        config.legacy_assembly = true;
526                        config.gas_estimates = true;
527                        config.method_identifiers = true;
528                        config.generated_sources = true;
529                        config.source_map = true;
530                        config.opcodes = true;
531                    }
532                    EvmOutputSelection::Assembly => {
533                        config.assembly = true;
534                    }
535                    EvmOutputSelection::LegacyAssembly => {
536                        config.legacy_assembly = true;
537                    }
538                    EvmOutputSelection::MethodIdentifiers => {
539                        config.method_identifiers = true;
540                    }
541                    EvmOutputSelection::GasEstimates => {
542                        config.gas_estimates = true;
543                    }
544                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::FunctionDebugData) => {
545                        config.function_debug_data = true;
546                    }
547                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes) => {
548                        config.opcodes = true;
549                    }
550                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
551                        config.generated_sources = true;
552                    }
553                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
554                        config.source_map = true;
555                    }
556                    _ => {}
557                },
558                ContractOutputSelection::Ewasm(_) => {
559                    config.ewasm = true;
560                }
561                ContractOutputSelection::IrOptimizedAst => {
562                    config.ir_optimized_ast = true;
563                }
564                ContractOutputSelection::TransientStorageLayout => {
565                    config.transient_storage_layout = true;
566                }
567                ContractOutputSelection::Abi => {}
568            }
569        }
570
571        config
572    }
573}
574
575/// Determines what to emit as an additional file
576#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
577pub struct ExtraOutputFiles {
578    pub abi: bool,
579    pub metadata: bool,
580    pub ir: bool,
581    pub ir_optimized: bool,
582    pub ewasm: bool,
583    pub assembly: bool,
584    pub legacy_assembly: bool,
585    pub source_map: bool,
586    pub generated_sources: bool,
587    pub bytecode: bool,
588    pub deployed_bytecode: bool,
589
590    /// PRIVATE: This structure may grow, As such, constructing this structure should
591    /// _always_ be done using a public constructor or update syntax:
592    ///
593    /// ```
594    /// use foundry_compilers::ExtraOutputFiles;
595    ///
596    /// let config = ExtraOutputFiles { metadata: true, ..Default::default() };
597    /// ```
598    #[doc(hidden)]
599    pub __non_exhaustive: (),
600}
601
602impl ExtraOutputFiles {
603    /// Returns an instance where all values are set to `true`
604    pub fn all() -> Self {
605        Self {
606            abi: true,
607            metadata: true,
608            ir: true,
609            ir_optimized: true,
610            ewasm: true,
611            assembly: true,
612            legacy_assembly: true,
613            source_map: true,
614            generated_sources: true,
615            bytecode: true,
616            deployed_bytecode: true,
617            __non_exhaustive: (),
618        }
619    }
620
621    /// Sets the values based on a set of `ContractOutputSelection`
622    pub fn from_output_selection(
623        settings: impl IntoIterator<Item = ContractOutputSelection>,
624    ) -> Self {
625        let mut config = Self::default();
626        for value in settings.into_iter() {
627            match value {
628                ContractOutputSelection::Abi => {
629                    config.abi = true;
630                }
631                ContractOutputSelection::Metadata => {
632                    config.metadata = true;
633                }
634                ContractOutputSelection::Ir => {
635                    config.ir = true;
636                }
637                ContractOutputSelection::IrOptimized => {
638                    config.ir_optimized = true;
639                }
640                ContractOutputSelection::Evm(evm) => match evm {
641                    EvmOutputSelection::All => {
642                        config.assembly = true;
643                        config.legacy_assembly = true;
644                        config.generated_sources = true;
645                        config.source_map = true;
646                        config.bytecode = true;
647                        config.deployed_bytecode = true;
648                    }
649                    EvmOutputSelection::Assembly => {
650                        config.assembly = true;
651                    }
652                    EvmOutputSelection::LegacyAssembly => {
653                        config.legacy_assembly = true;
654                    }
655                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
656                        config.generated_sources = true;
657                    }
658                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Object) => {
659                        config.bytecode = true;
660                    }
661                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
662                        config.source_map = true;
663                    }
664                    EvmOutputSelection::DeployedByteCode(DeployedBytecodeOutputSelection::All)
665                    | EvmOutputSelection::DeployedByteCode(
666                        DeployedBytecodeOutputSelection::Object,
667                    ) => {
668                        config.deployed_bytecode = true;
669                    }
670                    _ => {}
671                },
672                ContractOutputSelection::Ewasm(_) => {
673                    config.ewasm = true;
674                }
675                _ => {}
676            }
677        }
678        config
679    }
680
681    fn process_abi(&self, abi: Option<&JsonAbi>, file: &Path) -> Result<(), SolcError> {
682        if self.abi {
683            if let Some(abi) = abi {
684                let file = file.with_extension("abi.json");
685                fs::write(&file, serde_json::to_string_pretty(abi)?)
686                    .map_err(|err| SolcError::io(err, file))?
687            }
688        }
689        Ok(())
690    }
691
692    fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> {
693        if self.metadata {
694            if let Some(metadata) = metadata {
695                let file = file.with_extension("metadata.json");
696                fs::write(&file, serde_json::to_string_pretty(metadata)?)
697                    .map_err(|err| SolcError::io(err, file))?
698            }
699        }
700        Ok(())
701    }
702
703    fn process_ir(&self, ir: Option<&str>, file: &Path) -> Result<(), SolcError> {
704        if self.ir {
705            if let Some(ir) = ir {
706                let file = file.with_extension("ir");
707                fs::write(&file, ir).map_err(|err| SolcError::io(err, file))?
708            }
709        }
710        Ok(())
711    }
712
713    fn process_ir_optimized(
714        &self,
715        ir_optimized: Option<&str>,
716        file: &Path,
717    ) -> Result<(), SolcError> {
718        if self.ir_optimized {
719            if let Some(ir_optimized) = ir_optimized {
720                let file = file.with_extension("iropt");
721                fs::write(&file, ir_optimized).map_err(|err| SolcError::io(err, file))?
722            }
723        }
724        Ok(())
725    }
726
727    fn process_ewasm(&self, ewasm: Option<&Ewasm>, file: &Path) -> Result<(), SolcError> {
728        if self.ewasm {
729            if let Some(ewasm) = ewasm {
730                let file = file.with_extension("ewasm");
731                fs::write(&file, serde_json::to_vec_pretty(ewasm)?)
732                    .map_err(|err| SolcError::io(err, file))?;
733            }
734        }
735        Ok(())
736    }
737
738    fn process_assembly(&self, asm: Option<&str>, file: &Path) -> Result<(), SolcError> {
739        if self.assembly {
740            if let Some(asm) = asm {
741                let file = file.with_extension("asm");
742                fs::write(&file, asm).map_err(|err| SolcError::io(err, file))?
743            }
744        }
745        Ok(())
746    }
747
748    fn process_legacy_assembly(
749        &self,
750        asm: Option<serde_json::Value>,
751        file: &Path,
752    ) -> Result<(), SolcError> {
753        if self.legacy_assembly {
754            if let Some(legacy_asm) = asm {
755                let file = file.with_extension("legacyAssembly.json");
756                fs::write(&file, format!("{legacy_asm}")).map_err(|err| SolcError::io(err, file))?
757            }
758        }
759        Ok(())
760    }
761
762    fn process_generated_sources(
763        &self,
764        generated_sources: Option<&Vec<GeneratedSource>>,
765        file: &Path,
766    ) -> Result<(), SolcError> {
767        if self.generated_sources {
768            if let Some(generated_sources) = generated_sources {
769                let file = file.with_extension("gensources");
770                fs::write(&file, serde_json::to_vec_pretty(generated_sources)?)
771                    .map_err(|err| SolcError::io(err, file))?;
772            }
773        }
774        Ok(())
775    }
776
777    fn process_source_map(&self, source_map: Option<&str>, file: &Path) -> Result<(), SolcError> {
778        if self.source_map {
779            if let Some(source_map) = source_map {
780                let file = file.with_extension("sourcemap");
781                fs::write(&file, source_map).map_err(|err| SolcError::io(err, file))?
782            }
783        }
784        Ok(())
785    }
786
787    fn process_bytecode(
788        &self,
789        bytecode: Option<&BytecodeObject>,
790        file: &Path,
791    ) -> Result<(), SolcError> {
792        if self.bytecode {
793            if let Some(bytecode) = bytecode {
794                let code = hex::encode(bytecode.as_ref());
795                let file = file.with_extension("bin");
796                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
797            }
798        }
799        Ok(())
800    }
801
802    fn process_deployed_bytecode(
803        &self,
804        deployed: Option<&BytecodeObject>,
805        file: &Path,
806    ) -> Result<(), SolcError> {
807        if self.deployed_bytecode {
808            if let Some(deployed) = deployed {
809                let code = hex::encode(deployed.as_ref());
810                let file = file.with_extension("deployed-bin");
811                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
812            }
813        }
814        Ok(())
815    }
816
817    /// Write the set values as separate files
818    pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
819        self.process_abi(contract.abi.as_ref(), file)?;
820        self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?;
821        self.process_ir(contract.ir.as_deref(), file)?;
822        self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?;
823        self.process_ewasm(contract.ewasm.as_ref(), file)?;
824
825        let evm = contract.evm.as_ref();
826        self.process_assembly(evm.and_then(|evm| evm.assembly.as_deref()), file)?;
827        self.process_legacy_assembly(evm.and_then(|evm| evm.legacy_assembly.clone()), file)?;
828
829        let bytecode = evm.and_then(|evm| evm.bytecode.as_ref());
830        self.process_generated_sources(bytecode.map(|b| &b.generated_sources), file)?;
831
832        let deployed_bytecode = evm.and_then(|evm| evm.deployed_bytecode.as_ref());
833        self.process_source_map(bytecode.and_then(|b| b.source_map.as_deref()), file)?;
834        self.process_bytecode(bytecode.map(|b| &b.object), file)?;
835        self.process_deployed_bytecode(
836            deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object),
837            file,
838        )?;
839
840        Ok(())
841    }
842}