foundry_compilers_artifacts_solc/
contract.rs

1//! Contract related types.
2
3use crate::{
4    bytecode::{
5        Bytecode, BytecodeObject, CompactBytecode, CompactDeployedBytecode, DeployedBytecode,
6    },
7    serde_helpers, DevDoc, Evm, Ewasm, LosslessMetadata, Offsets, StorageLayout, UserDoc,
8};
9use alloy_json_abi::JsonAbi;
10use alloy_primitives::Bytes;
11use serde::{Deserialize, Serialize};
12use std::{borrow::Cow, collections::BTreeMap};
13
14/// Represents a compiled solidity contract
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub struct Contract {
18    /// The Ethereum Contract Metadata.
19    /// See <https://docs.soliditylang.org/en/develop/metadata.html>
20    pub abi: Option<JsonAbi>,
21    #[serde(
22        default,
23        skip_serializing_if = "Option::is_none",
24        with = "serde_helpers::json_string_opt"
25    )]
26    pub metadata: Option<LosslessMetadata>,
27    #[serde(default)]
28    pub userdoc: UserDoc,
29    #[serde(default)]
30    pub devdoc: DevDoc,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub ir: Option<String>,
33    #[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
34    pub storage_layout: StorageLayout,
35    #[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
36    pub transient_storage_layout: StorageLayout,
37    /// EVM-related outputs
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub evm: Option<Evm>,
40    /// Ewasm related outputs
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub ewasm: Option<Ewasm>,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub ir_optimized: Option<String>,
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub ir_optimized_ast: Option<serde_json::Value>,
47}
48
49impl<'a> From<&'a Contract> for CompactContractBytecodeCow<'a> {
50    fn from(artifact: &'a Contract) -> Self {
51        let (bytecode, deployed_bytecode) = if let Some(evm) = &artifact.evm {
52            (
53                evm.bytecode.clone().map(Into::into).map(Cow::Owned),
54                evm.deployed_bytecode.clone().map(Into::into).map(Cow::Owned),
55            )
56        } else {
57            (None, None)
58        };
59        CompactContractBytecodeCow {
60            abi: artifact.abi.as_ref().map(Cow::Borrowed),
61            bytecode,
62            deployed_bytecode,
63        }
64    }
65}
66
67/// Minimal representation of a contract with a present abi and bytecode.
68///
69/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
70/// `Bytecode` object.
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub struct ContractBytecode {
73    /// The Ethereum Contract ABI. If empty, it is represented as an empty
74    /// array. See <https://docs.soliditylang.org/en/develop/abi-spec.html>
75    pub abi: Option<JsonAbi>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub bytecode: Option<Bytecode>,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub deployed_bytecode: Option<DeployedBytecode>,
80}
81
82impl ContractBytecode {
83    /// Unwraps `self` into `ContractBytecodeSome`.
84    ///
85    /// # Panics
86    ///
87    /// Panics if any field is `None`.
88    #[track_caller]
89    pub fn unwrap(self) -> ContractBytecodeSome {
90        ContractBytecodeSome {
91            abi: self.abi.unwrap(),
92            bytecode: self.bytecode.unwrap(),
93            deployed_bytecode: self.deployed_bytecode.unwrap(),
94        }
95    }
96
97    /// Looks for all link references in deployment and runtime bytecodes
98    pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
99        let mut links = BTreeMap::new();
100        if let Some(bcode) = &self.bytecode {
101            links.extend(bcode.link_references.clone());
102        }
103
104        if let Some(d_bcode) = &self.deployed_bytecode {
105            if let Some(bcode) = &d_bcode.bytecode {
106                links.extend(bcode.link_references.clone());
107            }
108        }
109        links
110    }
111}
112
113impl From<Contract> for ContractBytecode {
114    fn from(c: Contract) -> Self {
115        let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
116            (evm.bytecode, evm.deployed_bytecode)
117        } else {
118            (None, None)
119        };
120
121        Self { abi: c.abi, bytecode, deployed_bytecode }
122    }
123}
124
125/// Minimal representation of a contract with a present abi and bytecode.
126///
127/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
128/// `Bytecode` object.
129#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct CompactContractBytecode {
132    /// The Ethereum Contract ABI. If empty, it is represented as an empty
133    /// array. See <https://docs.soliditylang.org/en/develop/abi-spec.html>
134    pub abi: Option<JsonAbi>,
135    #[serde(default, skip_serializing_if = "Option::is_none")]
136    pub bytecode: Option<CompactBytecode>,
137    #[serde(default, skip_serializing_if = "Option::is_none")]
138    pub deployed_bytecode: Option<CompactDeployedBytecode>,
139}
140
141impl CompactContractBytecode {
142    /// Looks for all link references in deployment and runtime bytecodes
143    pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
144        let mut links = BTreeMap::new();
145        if let Some(bcode) = &self.bytecode {
146            links.extend(bcode.link_references.clone());
147        }
148
149        if let Some(d_bcode) = &self.deployed_bytecode {
150            if let Some(bcode) = &d_bcode.bytecode {
151                links.extend(bcode.link_references.clone());
152            }
153        }
154        links
155    }
156}
157
158impl<'a> From<&'a CompactContractBytecode> for CompactContractBytecodeCow<'a> {
159    fn from(artifact: &'a CompactContractBytecode) -> Self {
160        CompactContractBytecodeCow {
161            abi: artifact.abi.as_ref().map(Cow::Borrowed),
162            bytecode: artifact.bytecode.as_ref().map(Cow::Borrowed),
163            deployed_bytecode: artifact.deployed_bytecode.as_ref().map(Cow::Borrowed),
164        }
165    }
166}
167
168impl From<Contract> for CompactContractBytecode {
169    fn from(c: Contract) -> Self {
170        let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
171            let evm = evm.into_compact();
172            (evm.bytecode, evm.deployed_bytecode)
173        } else {
174            (None, None)
175        };
176
177        Self { abi: c.abi, bytecode, deployed_bytecode }
178    }
179}
180
181impl From<ContractBytecode> for CompactContractBytecode {
182    fn from(c: ContractBytecode) -> Self {
183        let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) {
184            (Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
185            (None, Some(dbcode)) => (None, Some(dbcode.into())),
186            (Some(bcode), None) => (Some(bcode.into()), None),
187            (None, None) => (None, None),
188        };
189        Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime }
190    }
191}
192
193impl From<CompactContractBytecode> for ContractBytecode {
194    fn from(c: CompactContractBytecode) -> Self {
195        let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) {
196            (Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
197            (None, Some(dbcode)) => (None, Some(dbcode.into())),
198            (Some(bcode), None) => (Some(bcode.into()), None),
199            (None, None) => (None, None),
200        };
201        Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime }
202    }
203}
204
205/// A [CompactContractBytecode] that is either owns or borrows its content
206#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct CompactContractBytecodeCow<'a> {
209    pub abi: Option<Cow<'a, JsonAbi>>,
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub bytecode: Option<Cow<'a, CompactBytecode>>,
212    #[serde(default, skip_serializing_if = "Option::is_none")]
213    pub deployed_bytecode: Option<Cow<'a, CompactDeployedBytecode>>,
214}
215
216impl From<CompactContractBytecodeCow<'_>> for CompactContract {
217    fn from(value: CompactContractBytecodeCow<'_>) -> Self {
218        Self {
219            abi: value.abi.map(Cow::into_owned),
220            bin: value.bytecode.map(|bytecode| match bytecode {
221                Cow::Owned(bytecode) => bytecode.object,
222                Cow::Borrowed(bytecode) => bytecode.object.clone(),
223            }),
224            bin_runtime: value
225                .deployed_bytecode
226                .and_then(|bytecode| match bytecode {
227                    Cow::Owned(bytecode) => bytecode.bytecode,
228                    Cow::Borrowed(bytecode) => bytecode.bytecode.clone(),
229                })
230                .map(|bytecode| bytecode.object),
231        }
232    }
233}
234
235impl From<CompactContractBytecodeCow<'_>> for CompactContractBytecode {
236    fn from(value: CompactContractBytecodeCow<'_>) -> Self {
237        Self {
238            abi: value.abi.map(Cow::into_owned),
239            bytecode: value.bytecode.map(Cow::into_owned),
240            deployed_bytecode: value.deployed_bytecode.map(Cow::into_owned),
241        }
242    }
243}
244
245impl<'a> From<&'a CompactContractBytecodeCow<'_>> for CompactContractBytecodeCow<'a> {
246    fn from(value: &'a CompactContractBytecodeCow<'_>) -> Self {
247        Self {
248            abi: value.abi.as_ref().map(|x| Cow::Borrowed(&**x)),
249            bytecode: value.bytecode.as_ref().map(|x| Cow::Borrowed(&**x)),
250            deployed_bytecode: value.deployed_bytecode.as_ref().map(|x| Cow::Borrowed(&**x)),
251        }
252    }
253}
254
255/// Minimal representation of a contract with a present abi and bytecode.
256///
257/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
258/// `Bytecode` object.
259#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
260pub struct ContractBytecodeSome {
261    pub abi: JsonAbi,
262    pub bytecode: Bytecode,
263    pub deployed_bytecode: DeployedBytecode,
264}
265
266impl TryFrom<ContractBytecode> for ContractBytecodeSome {
267    type Error = ContractBytecode;
268
269    fn try_from(value: ContractBytecode) -> Result<Self, Self::Error> {
270        if value.abi.is_none() || value.bytecode.is_none() || value.deployed_bytecode.is_none() {
271            return Err(value);
272        }
273        Ok(value.unwrap())
274    }
275}
276
277/// Minimal representation of a contract's artifact with a present abi and bytecode.
278#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
279pub struct CompactContractSome {
280    /// The Ethereum Contract ABI. If empty, it is represented as an empty
281    /// array. See <https://docs.soliditylang.org/en/develop/abi-spec.html>
282    pub abi: JsonAbi,
283    pub bin: BytecodeObject,
284    #[serde(rename = "bin-runtime")]
285    pub bin_runtime: BytecodeObject,
286}
287
288impl TryFrom<CompactContract> for CompactContractSome {
289    type Error = CompactContract;
290
291    fn try_from(value: CompactContract) -> Result<Self, Self::Error> {
292        if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
293            return Err(value);
294        }
295        Ok(value.unwrap())
296    }
297}
298
299/// The general purpose minimal representation of a contract's abi with bytecode
300/// Unlike `CompactContractSome` all fields are optional so that every possible compiler output can
301/// be represented by it
302#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
303pub struct CompactContract {
304    /// The Ethereum Contract ABI. If empty, it is represented as an empty
305    /// array. See <https://docs.soliditylang.org/en/develop/abi-spec.html>
306    pub abi: Option<JsonAbi>,
307    #[serde(default, skip_serializing_if = "Option::is_none")]
308    pub bin: Option<BytecodeObject>,
309    #[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
310    pub bin_runtime: Option<BytecodeObject>,
311}
312
313impl CompactContract {
314    /// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode
315    pub fn into_parts(self) -> (Option<JsonAbi>, Option<Bytes>, Option<Bytes>) {
316        (
317            self.abi,
318            self.bin.and_then(|bin| bin.into_bytes()),
319            self.bin_runtime.and_then(|bin| bin.into_bytes()),
320        )
321    }
322
323    /// Returns the individual parts of this contract.
324    ///
325    /// If the values are `None`, then `Default` is returned.
326    pub fn into_parts_or_default(self) -> (JsonAbi, Bytes, Bytes) {
327        (
328            self.abi.unwrap_or_default(),
329            self.bin.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
330            self.bin_runtime.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
331        )
332    }
333
334    /// Unwraps `self` into `CompactContractSome`.
335    ///
336    /// # Panics
337    ///
338    /// Panics if any field is `None`.
339    #[track_caller]
340    pub fn unwrap(self) -> CompactContractSome {
341        CompactContractSome {
342            abi: self.abi.unwrap(),
343            bin: self.bin.unwrap(),
344            bin_runtime: self.bin_runtime.unwrap(),
345        }
346    }
347
348    /// Returns the `CompactContractSome` if any if the field equals `None` the `Default` value is
349    /// returned
350    ///
351    /// Unlike `unwrap`, this function does _not_ panic
352    pub fn unwrap_or_default(self) -> CompactContractSome {
353        CompactContractSome {
354            abi: self.abi.unwrap_or_default(),
355            bin: self.bin.unwrap_or_default(),
356            bin_runtime: self.bin_runtime.unwrap_or_default(),
357        }
358    }
359}
360
361impl From<serde_json::Value> for CompactContract {
362    fn from(mut val: serde_json::Value) -> Self {
363        if let Some(map) = val.as_object_mut() {
364            let abi = map.remove("abi").and_then(|val| serde_json::from_value(val).ok());
365            let bin = map.remove("bin").and_then(|val| serde_json::from_value(val).ok());
366            let bin_runtime =
367                map.remove("bin-runtime").and_then(|val| serde_json::from_value(val).ok());
368            Self { abi, bin, bin_runtime }
369        } else {
370            Self::default()
371        }
372    }
373}
374
375impl<'a> From<&'a serde_json::Value> for CompactContractBytecodeCow<'a> {
376    fn from(artifact: &'a serde_json::Value) -> Self {
377        let c = CompactContractBytecode::from(artifact.clone());
378        CompactContractBytecodeCow {
379            abi: c.abi.map(Cow::Owned),
380            bytecode: c.bytecode.map(Cow::Owned),
381            deployed_bytecode: c.deployed_bytecode.map(Cow::Owned),
382        }
383    }
384}
385
386impl From<serde_json::Value> for CompactContractBytecode {
387    fn from(val: serde_json::Value) -> Self {
388        serde_json::from_value(val).unwrap_or_default()
389    }
390}
391
392impl From<ContractBytecode> for CompactContract {
393    fn from(c: ContractBytecode) -> Self {
394        let ContractBytecode { abi, bytecode, deployed_bytecode } = c;
395        Self {
396            abi,
397            bin: bytecode.map(|c| c.object),
398            bin_runtime: deployed_bytecode
399                .and_then(|deployed| deployed.bytecode.map(|code| code.object)),
400        }
401    }
402}
403
404impl From<CompactContractBytecode> for CompactContract {
405    fn from(c: CompactContractBytecode) -> Self {
406        let c: ContractBytecode = c.into();
407        c.into()
408    }
409}
410
411impl From<ContractBytecodeSome> for CompactContract {
412    fn from(c: ContractBytecodeSome) -> Self {
413        Self {
414            abi: Some(c.abi),
415            bin: Some(c.bytecode.object),
416            bin_runtime: c.deployed_bytecode.bytecode.map(|code| code.object),
417        }
418    }
419}
420
421impl From<Contract> for CompactContract {
422    fn from(c: Contract) -> Self {
423        ContractBytecode::from(c).into()
424    }
425}
426
427impl From<CompactContractSome> for CompactContract {
428    fn from(c: CompactContractSome) -> Self {
429        Self { abi: Some(c.abi), bin: Some(c.bin), bin_runtime: Some(c.bin_runtime) }
430    }
431}
432
433impl<'a> From<CompactContractRef<'a>> for CompactContract {
434    fn from(c: CompactContractRef<'a>) -> Self {
435        Self { abi: c.abi.cloned(), bin: c.bin.cloned(), bin_runtime: c.bin_runtime.cloned() }
436    }
437}
438
439impl<'a> From<CompactContractRefSome<'a>> for CompactContract {
440    fn from(c: CompactContractRefSome<'a>) -> Self {
441        Self {
442            abi: Some(c.abi.clone()),
443            bin: Some(c.bin.clone()),
444            bin_runtime: Some(c.bin_runtime.clone()),
445        }
446    }
447}
448
449/// Minimal representation of a contract with a present abi and bytecode that borrows.
450#[derive(Clone, Copy, Debug, Serialize)]
451pub struct CompactContractRefSome<'a> {
452    pub abi: &'a JsonAbi,
453    pub bin: &'a BytecodeObject,
454    #[serde(rename = "bin-runtime")]
455    pub bin_runtime: &'a BytecodeObject,
456}
457
458impl CompactContractRefSome<'_> {
459    /// Returns the individual parts of this contract.
460    ///
461    /// If the values are `None`, then `Default` is returned.
462    pub fn into_parts(self) -> (JsonAbi, Bytes, Bytes) {
463        CompactContract::from(self).into_parts_or_default()
464    }
465}
466
467impl<'a> TryFrom<CompactContractRef<'a>> for CompactContractRefSome<'a> {
468    type Error = CompactContractRef<'a>;
469
470    fn try_from(value: CompactContractRef<'a>) -> Result<Self, Self::Error> {
471        if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
472            return Err(value);
473        }
474        Ok(value.unwrap())
475    }
476}
477
478/// Helper type to serialize while borrowing from `Contract`
479#[derive(Clone, Copy, Debug, Serialize)]
480pub struct CompactContractRef<'a> {
481    pub abi: Option<&'a JsonAbi>,
482    #[serde(default, skip_serializing_if = "Option::is_none")]
483    pub bin: Option<&'a BytecodeObject>,
484    #[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
485    pub bin_runtime: Option<&'a BytecodeObject>,
486}
487
488impl<'a> CompactContractRef<'a> {
489    /// Clones the referenced values and returns as tuples
490    pub fn into_parts(self) -> (Option<JsonAbi>, Option<Bytes>, Option<Bytes>) {
491        CompactContract::from(self).into_parts()
492    }
493
494    /// Returns the individual parts of this contract.
495    ///
496    /// If the values are `None`, then `Default` is returned.
497    pub fn into_parts_or_default(self) -> (JsonAbi, Bytes, Bytes) {
498        CompactContract::from(self).into_parts_or_default()
499    }
500
501    pub fn bytecode(&self) -> Option<&Bytes> {
502        self.bin.as_ref().and_then(|bin| bin.as_bytes())
503    }
504
505    pub fn runtime_bytecode(&self) -> Option<&Bytes> {
506        self.bin_runtime.as_ref().and_then(|bin| bin.as_bytes())
507    }
508
509    /// Unwraps `self` into `CompactContractRefSome`.
510    ///
511    /// # Panics
512    ///
513    /// Panics if any field is `None`.
514    #[track_caller]
515    pub fn unwrap(self) -> CompactContractRefSome<'a> {
516        CompactContractRefSome {
517            abi: self.abi.unwrap(),
518            bin: self.bin.unwrap(),
519            bin_runtime: self.bin_runtime.unwrap(),
520        }
521    }
522}
523
524impl<'a> From<&'a Contract> for CompactContractRef<'a> {
525    fn from(c: &'a Contract) -> Self {
526        let (bin, bin_runtime) = if let Some(evm) = &c.evm {
527            (
528                evm.bytecode.as_ref().map(|c| &c.object),
529                evm.deployed_bytecode
530                    .as_ref()
531                    .and_then(|deployed| deployed.bytecode.as_ref().map(|evm| &evm.object)),
532            )
533        } else {
534            (None, None)
535        };
536
537        Self { abi: c.abi.as_ref(), bin, bin_runtime }
538    }
539}