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 legacy_assembly && artifact.legacy_assembly.is_none() {
381            return Ok(true);
382        }
383        if source_map && artifact.get_source_map_str().is_none() {
384            return Ok(true);
385        }
386        if generated_sources {
387            // We can't check if generated sources are missing or just empty.
388            return Ok(true);
389        }
390        Ok(false)
391    }
392
393    /// Writes extra files for cached artifacts based on [Self::additional_files].
394    fn handle_cached_artifacts(
395        &self,
396        artifacts: &crate::Artifacts<Self::Artifact>,
397    ) -> Result<(), SolcError> {
398        for artifacts in artifacts.values() {
399            for artifacts in artifacts.values() {
400                for artifact_file in artifacts {
401                    let file = &artifact_file.file;
402                    let artifact = &artifact_file.artifact;
403                    self.additional_files.process_abi(artifact.abi.as_ref(), file)?;
404                    self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?;
405                    self.additional_files
406                        .process_legacy_assembly(artifact.legacy_assembly.clone(), file)?;
407                    self.additional_files
408                        .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?;
409                    self.additional_files.process_deployed_bytecode(
410                        artifact
411                            .deployed_bytecode
412                            .as_ref()
413                            .and_then(|d| d.bytecode.as_ref())
414                            .map(|b| &b.object),
415                        file,
416                    )?;
417                    self.additional_files
418                        .process_generated_sources(Some(&artifact.generated_sources), file)?;
419                    self.additional_files.process_ir(artifact.ir.as_deref(), file)?;
420                    self.additional_files
421                        .process_ir_optimized(artifact.ir_optimized.as_deref(), file)?;
422                    self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?;
423                    self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?;
424                    self.additional_files
425                        .process_source_map(artifact.get_source_map_str().as_deref(), file)?;
426                }
427            }
428        }
429
430        Ok(())
431    }
432}
433
434/// Determines the additional values to include in the contract's artifact file
435#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
436pub struct ExtraOutputValues {
437    pub ast: bool,
438    pub userdoc: bool,
439    pub devdoc: bool,
440    pub method_identifiers: bool,
441    pub storage_layout: bool,
442    pub transient_storage_layout: bool,
443    pub assembly: bool,
444    pub legacy_assembly: bool,
445    pub gas_estimates: bool,
446    pub metadata: bool,
447    pub ir: bool,
448    pub ir_optimized: bool,
449    pub ir_optimized_ast: bool,
450    pub ewasm: bool,
451    pub function_debug_data: bool,
452    pub generated_sources: bool,
453    pub source_map: bool,
454    pub opcodes: bool,
455
456    /// PRIVATE: This structure may grow, As such, constructing this structure should
457    /// _always_ be done using a public constructor or update syntax:
458    ///
459    /// ```
460    /// use foundry_compilers::ExtraOutputValues;
461    ///
462    /// let config = ExtraOutputValues { ir: true, ..Default::default() };
463    /// ```
464    #[doc(hidden)]
465    pub __non_exhaustive: (),
466}
467
468impl ExtraOutputValues {
469    /// Returns an instance where all values are set to `true`
470    pub fn all() -> Self {
471        Self {
472            ast: true,
473            userdoc: true,
474            devdoc: true,
475            method_identifiers: true,
476            storage_layout: true,
477            transient_storage_layout: true,
478            assembly: true,
479            legacy_assembly: true,
480            gas_estimates: true,
481            metadata: true,
482            ir: true,
483            ir_optimized: true,
484            ir_optimized_ast: true,
485            ewasm: true,
486            function_debug_data: true,
487            generated_sources: true,
488            source_map: true,
489            opcodes: true,
490            __non_exhaustive: (),
491        }
492    }
493
494    /// Sets the values based on a set of `ContractOutputSelection`
495    pub fn from_output_selection(
496        settings: impl IntoIterator<Item = ContractOutputSelection>,
497    ) -> Self {
498        let mut config = Self::default();
499        for value in settings.into_iter() {
500            match value {
501                ContractOutputSelection::DevDoc => {
502                    config.devdoc = true;
503                }
504                ContractOutputSelection::UserDoc => {
505                    config.userdoc = true;
506                }
507                ContractOutputSelection::Metadata => {
508                    config.metadata = true;
509                }
510                ContractOutputSelection::Ir => {
511                    config.ir = true;
512                }
513                ContractOutputSelection::IrOptimized => {
514                    config.ir_optimized = true;
515                }
516                ContractOutputSelection::StorageLayout => {
517                    config.storage_layout = true;
518                }
519                ContractOutputSelection::Evm(evm) => match evm {
520                    EvmOutputSelection::All => {
521                        config.assembly = true;
522                        config.legacy_assembly = true;
523                        config.gas_estimates = true;
524                        config.method_identifiers = true;
525                        config.generated_sources = true;
526                        config.source_map = true;
527                        config.opcodes = true;
528                    }
529                    EvmOutputSelection::Assembly => {
530                        config.assembly = true;
531                    }
532                    EvmOutputSelection::LegacyAssembly => {
533                        config.legacy_assembly = true;
534                    }
535                    EvmOutputSelection::MethodIdentifiers => {
536                        config.method_identifiers = true;
537                    }
538                    EvmOutputSelection::GasEstimates => {
539                        config.gas_estimates = true;
540                    }
541                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::FunctionDebugData) => {
542                        config.function_debug_data = true;
543                    }
544                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes) => {
545                        config.opcodes = true;
546                    }
547                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
548                        config.generated_sources = true;
549                    }
550                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
551                        config.source_map = true;
552                    }
553                    _ => {}
554                },
555                ContractOutputSelection::Ewasm(_) => {
556                    config.ewasm = true;
557                }
558                ContractOutputSelection::IrOptimizedAst => {
559                    config.ir_optimized_ast = true;
560                }
561                ContractOutputSelection::TransientStorageLayout => {
562                    config.transient_storage_layout = true;
563                }
564                ContractOutputSelection::Abi => {}
565            }
566        }
567
568        config
569    }
570}
571
572/// Determines what to emit as an additional file
573#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
574pub struct ExtraOutputFiles {
575    pub abi: bool,
576    pub metadata: bool,
577    pub ir: bool,
578    pub ir_optimized: bool,
579    pub ewasm: bool,
580    pub assembly: bool,
581    pub legacy_assembly: bool,
582    pub source_map: bool,
583    pub generated_sources: bool,
584    pub bytecode: bool,
585    pub deployed_bytecode: bool,
586
587    /// PRIVATE: This structure may grow, As such, constructing this structure should
588    /// _always_ be done using a public constructor or update syntax:
589    ///
590    /// ```
591    /// use foundry_compilers::ExtraOutputFiles;
592    ///
593    /// let config = ExtraOutputFiles { metadata: true, ..Default::default() };
594    /// ```
595    #[doc(hidden)]
596    pub __non_exhaustive: (),
597}
598
599impl ExtraOutputFiles {
600    /// Returns an instance where all values are set to `true`
601    pub fn all() -> Self {
602        Self {
603            abi: true,
604            metadata: true,
605            ir: true,
606            ir_optimized: true,
607            ewasm: true,
608            assembly: true,
609            legacy_assembly: true,
610            source_map: true,
611            generated_sources: true,
612            bytecode: true,
613            deployed_bytecode: true,
614            __non_exhaustive: (),
615        }
616    }
617
618    /// Sets the values based on a set of `ContractOutputSelection`
619    pub fn from_output_selection(
620        settings: impl IntoIterator<Item = ContractOutputSelection>,
621    ) -> Self {
622        let mut config = Self::default();
623        for value in settings.into_iter() {
624            match value {
625                ContractOutputSelection::Abi => {
626                    config.abi = true;
627                }
628                ContractOutputSelection::Metadata => {
629                    config.metadata = true;
630                }
631                ContractOutputSelection::Ir => {
632                    config.ir = true;
633                }
634                ContractOutputSelection::IrOptimized => {
635                    config.ir_optimized = true;
636                }
637                ContractOutputSelection::Evm(evm) => match evm {
638                    EvmOutputSelection::All => {
639                        config.assembly = true;
640                        config.legacy_assembly = true;
641                        config.generated_sources = true;
642                        config.source_map = true;
643                        config.bytecode = true;
644                        config.deployed_bytecode = true;
645                    }
646                    EvmOutputSelection::Assembly => {
647                        config.assembly = true;
648                    }
649                    EvmOutputSelection::LegacyAssembly => {
650                        config.legacy_assembly = true;
651                    }
652                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
653                        config.generated_sources = true;
654                    }
655                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Object) => {
656                        config.bytecode = true;
657                    }
658                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
659                        config.source_map = true;
660                    }
661                    EvmOutputSelection::DeployedByteCode(DeployedBytecodeOutputSelection::All)
662                    | EvmOutputSelection::DeployedByteCode(
663                        DeployedBytecodeOutputSelection::Object,
664                    ) => {
665                        config.deployed_bytecode = true;
666                    }
667                    _ => {}
668                },
669                ContractOutputSelection::Ewasm(_) => {
670                    config.ewasm = true;
671                }
672                _ => {}
673            }
674        }
675        config
676    }
677
678    fn process_abi(&self, abi: Option<&JsonAbi>, file: &Path) -> Result<(), SolcError> {
679        if self.abi {
680            if let Some(abi) = abi {
681                let file = file.with_extension("abi.json");
682                fs::write(&file, serde_json::to_string_pretty(abi)?)
683                    .map_err(|err| SolcError::io(err, file))?
684            }
685        }
686        Ok(())
687    }
688
689    fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> {
690        if self.metadata {
691            if let Some(metadata) = metadata {
692                let file = file.with_extension("metadata.json");
693                fs::write(&file, serde_json::to_string_pretty(metadata)?)
694                    .map_err(|err| SolcError::io(err, file))?
695            }
696        }
697        Ok(())
698    }
699
700    fn process_ir(&self, ir: Option<&str>, file: &Path) -> Result<(), SolcError> {
701        if self.ir {
702            if let Some(ir) = ir {
703                let file = file.with_extension("ir");
704                fs::write(&file, ir).map_err(|err| SolcError::io(err, file))?
705            }
706        }
707        Ok(())
708    }
709
710    fn process_ir_optimized(
711        &self,
712        ir_optimized: Option<&str>,
713        file: &Path,
714    ) -> Result<(), SolcError> {
715        if self.ir_optimized {
716            if let Some(ir_optimized) = ir_optimized {
717                let file = file.with_extension("iropt");
718                fs::write(&file, ir_optimized).map_err(|err| SolcError::io(err, file))?
719            }
720        }
721        Ok(())
722    }
723
724    fn process_ewasm(&self, ewasm: Option<&Ewasm>, file: &Path) -> Result<(), SolcError> {
725        if self.ewasm {
726            if let Some(ewasm) = ewasm {
727                let file = file.with_extension("ewasm");
728                fs::write(&file, serde_json::to_vec_pretty(ewasm)?)
729                    .map_err(|err| SolcError::io(err, file))?;
730            }
731        }
732        Ok(())
733    }
734
735    fn process_assembly(&self, asm: Option<&str>, file: &Path) -> Result<(), SolcError> {
736        if self.assembly {
737            if let Some(asm) = asm {
738                let file = file.with_extension("asm");
739                fs::write(&file, asm).map_err(|err| SolcError::io(err, file))?
740            }
741        }
742        Ok(())
743    }
744
745    fn process_legacy_assembly(
746        &self,
747        asm: Option<serde_json::Value>,
748        file: &Path,
749    ) -> Result<(), SolcError> {
750        if self.legacy_assembly {
751            if let Some(legacy_asm) = asm {
752                let file = file.with_extension("legacyAssembly.json");
753                fs::write(&file, format!("{legacy_asm}")).map_err(|err| SolcError::io(err, file))?
754            }
755        }
756        Ok(())
757    }
758
759    fn process_generated_sources(
760        &self,
761        generated_sources: Option<&Vec<GeneratedSource>>,
762        file: &Path,
763    ) -> Result<(), SolcError> {
764        if self.generated_sources {
765            if let Some(generated_sources) = generated_sources {
766                let file = file.with_extension("gensources");
767                fs::write(&file, serde_json::to_vec_pretty(generated_sources)?)
768                    .map_err(|err| SolcError::io(err, file))?;
769            }
770        }
771        Ok(())
772    }
773
774    fn process_source_map(&self, source_map: Option<&str>, file: &Path) -> Result<(), SolcError> {
775        if self.source_map {
776            if let Some(source_map) = source_map {
777                let file = file.with_extension("sourcemap");
778                fs::write(&file, source_map).map_err(|err| SolcError::io(err, file))?
779            }
780        }
781        Ok(())
782    }
783
784    fn process_bytecode(
785        &self,
786        bytecode: Option<&BytecodeObject>,
787        file: &Path,
788    ) -> Result<(), SolcError> {
789        if self.bytecode {
790            if let Some(bytecode) = bytecode {
791                let code = hex::encode(bytecode.as_ref());
792                let file = file.with_extension("bin");
793                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
794            }
795        }
796        Ok(())
797    }
798
799    fn process_deployed_bytecode(
800        &self,
801        deployed: Option<&BytecodeObject>,
802        file: &Path,
803    ) -> Result<(), SolcError> {
804        if self.deployed_bytecode {
805            if let Some(deployed) = deployed {
806                let code = hex::encode(deployed.as_ref());
807                let file = file.with_extension("deployed-bin");
808                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
809            }
810        }
811        Ok(())
812    }
813
814    /// Write the set values as separate files
815    pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
816        self.process_abi(contract.abi.as_ref(), file)?;
817        self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?;
818        self.process_ir(contract.ir.as_deref(), file)?;
819        self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?;
820        self.process_ewasm(contract.ewasm.as_ref(), file)?;
821
822        let evm = contract.evm.as_ref();
823        self.process_assembly(evm.and_then(|evm| evm.assembly.as_deref()), file)?;
824        self.process_legacy_assembly(evm.and_then(|evm| evm.legacy_assembly.clone()), file)?;
825
826        let bytecode = evm.and_then(|evm| evm.bytecode.as_ref());
827        self.process_generated_sources(bytecode.map(|b| &b.generated_sources), file)?;
828
829        let deployed_bytecode = evm.and_then(|evm| evm.deployed_bytecode.as_ref());
830        self.process_source_map(bytecode.and_then(|b| b.source_map.as_deref()), file)?;
831        self.process_bytecode(bytecode.map(|b| &b.object), file)?;
832        self.process_deployed_bytecode(
833            deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object),
834            file,
835        )?;
836
837        Ok(())
838    }
839}