diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs index b537e6a9..74bee85a 100644 --- a/ethers-solc/src/artifact_output/configurable.rs +++ b/ethers-solc/src/artifact_output/configurable.rs @@ -5,13 +5,13 @@ use crate::{ bytecode::{CompactBytecode, CompactDeployedBytecode}, contract::{CompactContract, CompactContractBytecode, Contract}, output_selection::{ContractOutputSelection, EvmOutputSelection, EwasmOutputSelection}, - CompactEvm, DevDoc, Ewasm, GasEstimates, LosslessAbi, Metadata, Offsets, Settings, - StorageLayout, UserDoc, + CompactContractBytecodeCow, CompactEvm, DevDoc, Ewasm, GasEstimates, LosslessAbi, Metadata, + Offsets, Settings, StorageLayout, UserDoc, }, ArtifactOutput, SolcConfig, SolcError, }; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fs, path::Path}; +use std::{borrow::Cow, collections::BTreeMap, fs, path::Path}; /// Represents the `Artifact` that `ConfigurableArtifacts` emits. /// @@ -87,6 +87,16 @@ impl From for CompactContract { } } +impl<'a> From<&'a ConfigurableContractArtifact> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a ConfigurableContractArtifact) -> Self { + CompactContractBytecodeCow { + abi: artifact.abi.as_ref().map(|abi| Cow::Borrowed(&abi.abi)), + bytecode: artifact.bytecode.as_ref().map(Cow::Borrowed), + deployed_bytecode: artifact.deployed_bytecode.as_ref().map(Cow::Borrowed), + } + } +} + /// An `Artifact` implementation that can be configured to include additional content and emit /// additional files /// diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs index 25d9790b..4d6f4623 100644 --- a/ethers-solc/src/artifact_output/mod.rs +++ b/ethers-solc/src/artifact_output/mod.rs @@ -8,13 +8,17 @@ use ethers_core::{abi::Abi, types::Bytes}; use semver::Version; use serde::{de::DeserializeOwned, Serialize}; use std::{ + borrow::Cow, collections::btree_map::BTreeMap, fmt, fs, io, path::{Path, PathBuf}, }; mod configurable; -use crate::artifacts::contract::{CompactContract, CompactContractBytecode, Contract}; +use crate::artifacts::{ + contract::{CompactContract, CompactContractBytecode, Contract}, + BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, +}; pub use configurable::*; /// Represents unique artifact metadata for identifying artifacts on output @@ -290,6 +294,29 @@ pub trait Artifact { /// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode fn into_parts(self) -> (Option, Option, Option); + /// Consumes the type and returns the [Abi] + fn into_abi(self) -> Option + where + Self: Sized, + { + self.into_parts().0 + } + + /// Consumes the type and returns the `bytecode` + fn into_bytecode_bytes(self) -> Option + where + Self: Sized, + { + self.into_parts().1 + } + /// Consumes the type and returns the `deployed bytecode` + fn into_deployed_bytecode_bytes(self) -> Option + where + Self: Sized, + { + self.into_parts().2 + } + /// Same as [`Self::into_parts()`] but returns `Err` if an element is `None` fn try_into_parts(self) -> Result<(Abi, Bytes, Bytes)> where @@ -303,11 +330,67 @@ pub trait Artifact { deployed_bytecode.ok_or_else(|| SolcError::msg("deployed bytecode missing"))?, )) } + + /// Returns the reference of container type for abi, compact bytecode and deployed bytecode if + /// available + fn get_contract_bytecode(&self) -> CompactContractBytecodeCow; + + /// Returns the reference to the `bytecode` + fn get_bytecode(&self) -> Option> { + self.get_contract_bytecode().bytecode + } + + /// Returns the reference to the `bytecode` object + fn get_bytecode_object(&self) -> Option> { + let val = match self.get_bytecode()? { + Cow::Borrowed(b) => Cow::Borrowed(&b.object), + Cow::Owned(b) => Cow::Owned(b.object), + }; + Some(val) + } + + /// Returns the bytes of the `bytecode` object + fn get_bytecode_bytes(&self) -> Option> { + let val = match self.get_bytecode_object()? { + Cow::Borrowed(b) => Cow::Borrowed(b.as_bytes()?), + Cow::Owned(b) => Cow::Owned(b.into_bytes()?), + }; + Some(val) + } + + /// Returns the reference to the `deployedBytecode` + fn get_deployed_bytecode(&self) -> Option> { + self.get_contract_bytecode().deployed_bytecode + } + + /// Returns the reference to the `bytecode` object + fn get_deployed_bytecode_object(&self) -> Option> { + let val = match self.get_deployed_bytecode()? { + Cow::Borrowed(b) => Cow::Borrowed(&b.bytecode.as_ref()?.object), + Cow::Owned(b) => Cow::Owned(b.bytecode?.object), + }; + Some(val) + } + + /// Returns the bytes of the `deployed bytecode` object + fn get_deployed_bytecode_bytes(&self) -> Option> { + let val = match self.get_deployed_bytecode_object()? { + Cow::Borrowed(b) => Cow::Borrowed(b.as_bytes()?), + Cow::Owned(b) => Cow::Owned(b.into_bytes()?), + }; + Some(val) + } + + /// Returns the reference to the [Abi] if available + fn get_abi(&self) -> Option> { + self.get_contract_bytecode().abi + } } impl Artifact for T where T: Into + Into, + for<'a> &'a T: Into>, { fn into_inner(self) -> (Option, Option) { let artifact = self.into_compact_contract(); @@ -325,6 +408,10 @@ where fn into_parts(self) -> (Option, Option, Option) { self.into_compact_contract().into_parts() } + + fn get_contract_bytecode(&self) -> CompactContractBytecodeCow { + self.into() + } } /// Handler invoked with the output of `solc` diff --git a/ethers-solc/src/artifacts/bytecode.rs b/ethers-solc/src/artifacts/bytecode.rs index e38951fa..b8814177 100644 --- a/ethers-solc/src/artifacts/bytecode.rs +++ b/ethers-solc/src/artifacts/bytecode.rs @@ -221,6 +221,14 @@ impl BytecodeObject { BytecodeObject::Unlinked(_) => None, } } + + /// Returns the number of bytes of the fully linked bytecode + /// + /// Returns `0` if this object is unlinked. + pub fn bytes_len(&self) -> usize { + self.as_bytes().map(|b| b.as_ref().len()).unwrap_or_default() + } + /// Returns a reference to the underlying `String` if the object is unlinked pub fn as_str(&self) -> Option<&str> { match self { diff --git a/ethers-solc/src/artifacts/contract.rs b/ethers-solc/src/artifacts/contract.rs index f1b6b9bf..12a4717a 100644 --- a/ethers-solc/src/artifacts/contract.rs +++ b/ethers-solc/src/artifacts/contract.rs @@ -8,7 +8,7 @@ use crate::artifacts::{ }; use ethers_core::{abi::Contract as Abi, types::Bytes}; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, convert::TryFrom}; +use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; /// Represents a compiled solidity contract #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -41,6 +41,24 @@ pub struct Contract { pub ir_optimized: Option, } +impl<'a> From<&'a Contract> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a Contract) -> Self { + let (bytecode, deployed_bytecode) = if let Some(ref evm) = artifact.evm { + ( + evm.bytecode.clone().map(Into::into).map(Cow::Owned), + evm.deployed_bytecode.clone().map(Into::into).map(Cow::Owned), + ) + } else { + (None, None) + }; + CompactContractBytecodeCow { + abi: artifact.abi.as_ref().map(|abi| Cow::Borrowed(&abi.abi)), + bytecode, + deployed_bytecode, + } + } +} + /// Minimal representation of a contract with a present abi and bytecode. /// /// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole @@ -143,6 +161,16 @@ impl CompactContractBytecode { } } +impl<'a> From<&'a CompactContractBytecode> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a CompactContractBytecode) -> Self { + CompactContractBytecodeCow { + abi: artifact.abi.as_ref().map(Cow::Borrowed), + bytecode: artifact.bytecode.as_ref().map(Cow::Borrowed), + deployed_bytecode: artifact.deployed_bytecode.as_ref().map(Cow::Borrowed), + } + } +} + impl From for CompactContractBytecode { fn from(c: Contract) -> Self { let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm { @@ -180,6 +208,17 @@ impl From for ContractBytecode { } } +/// A [CompactContractBytecode] that is either owns or borrows its content +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CompactContractBytecodeCow<'a> { + pub abi: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bytecode: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option>, +} + /// Minimal representation of a contract with a present abi and bytecode. /// /// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole @@ -311,6 +350,17 @@ impl From for CompactContract { } } +impl<'a> From<&'a serde_json::Value> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a serde_json::Value) -> Self { + let c = CompactContractBytecode::from(artifact.clone()); + CompactContractBytecodeCow { + abi: c.abi.map(Cow::Owned), + bytecode: c.bytecode.map(Cow::Owned), + deployed_bytecode: c.deployed_bytecode.map(Cow::Owned), + } + } +} + impl From for CompactContractBytecode { fn from(val: serde_json::Value) -> Self { serde_json::from_value(val).unwrap_or_default() diff --git a/ethers-solc/src/hh.rs b/ethers-solc/src/hh.rs index dca4f95a..bf215a3f 100644 --- a/ethers-solc/src/hh.rs +++ b/ethers-solc/src/hh.rs @@ -4,12 +4,12 @@ use crate::{ artifacts::{ bytecode::{Bytecode, BytecodeObject, DeployedBytecode}, contract::{CompactContract, CompactContractBytecode, Contract, ContractBytecode}, - LosslessAbi, Offsets, + CompactContractBytecodeCow, LosslessAbi, Offsets, }, ArtifactOutput, }; use serde::{Deserialize, Serialize}; -use std::collections::btree_map::BTreeMap; +use std::{borrow::Cow, collections::btree_map::BTreeMap}; const HH_ARTIFACT_VERSION: &str = "hh-sol-artifact-1"; @@ -41,6 +41,17 @@ pub struct HardhatArtifact { pub deployed_link_references: BTreeMap>>, } +impl<'a> From<&'a HardhatArtifact> for CompactContractBytecodeCow<'a> { + fn from(artifact: &'a HardhatArtifact) -> Self { + let c: ContractBytecode = artifact.clone().into(); + CompactContractBytecodeCow { + abi: Some(Cow::Borrowed(&artifact.abi.abi)), + bytecode: c.bytecode.map(|b| Cow::Owned(b.into())), + deployed_bytecode: c.deployed_bytecode.map(|b| Cow::Owned(b.into())), + } + } +} + impl From for CompactContract { fn from(artifact: HardhatArtifact) -> Self { CompactContract {