Skip to main content

foundry_compilers_artifacts_solc/
contract.rs

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