ethers_solc/artifacts/
contract.rs

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