write `CompactContractBytecode` instead of `CompactContract` (#833)

* write `CompactContractBytecode` instead of `CompactContract`

* chore: fix doctest

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
brockelmore 2022-01-28 02:23:28 -05:00 committed by GitHub
parent ec2b51539d
commit 24c39bd32a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 264 additions and 10 deletions

View File

@ -814,6 +814,21 @@ impl ContractBytecode {
deployed_bytecode: self.deployed_bytecode.unwrap(),
}
}
/// Looks for all link references in deployment and runtime bytecodes
pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
let mut links = BTreeMap::new();
if let Some(bcode) = &self.bytecode {
links.extend(bcode.link_references.clone());
}
if let Some(d_bcode) = &self.deployed_bytecode {
if let Some(bcode) = &d_bcode.bytecode {
links.extend(bcode.link_references.clone());
}
}
links
}
}
impl From<Contract> for ContractBytecode {
@ -828,6 +843,80 @@ impl From<Contract> for ContractBytecode {
}
}
/// Minimal representation of a contract with a present abi and bytecode.
///
/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
/// `Bytecode` object.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct CompactContractBytecode {
/// The Ethereum Contract ABI. If empty, it is represented as an empty
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
pub abi: Option<Abi>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bytecode: Option<CompactBytecode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deployed_bytecode: Option<CompactDeployedBytecode>,
}
impl CompactContractBytecode {
/// Looks for all link references in deployment and runtime bytecodes
pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
let mut links = BTreeMap::new();
if let Some(bcode) = &self.bytecode {
links.extend(bcode.link_references.clone());
}
if let Some(d_bcode) = &self.deployed_bytecode {
if let Some(bcode) = &d_bcode.bytecode {
links.extend(bcode.link_references.clone());
}
}
links
}
}
impl From<Contract> for CompactContractBytecode {
fn from(c: Contract) -> Self {
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
let (maybe_bcode, maybe_runtime) = match (evm.bytecode, evm.deployed_bytecode) {
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
(None, Some(dbcode)) => (None, Some(dbcode.into())),
(Some(bcode), None) => (Some(bcode.into()), None),
(None, None) => (None, None),
};
(maybe_bcode, maybe_runtime)
} else {
(None, None)
};
Self { abi: c.abi, bytecode, deployed_bytecode }
}
}
impl From<ContractBytecode> for CompactContractBytecode {
fn from(c: ContractBytecode) -> Self {
let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) {
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
(None, Some(dbcode)) => (None, Some(dbcode.into())),
(Some(bcode), None) => (Some(bcode.into()), None),
(None, None) => (None, None),
};
Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime }
}
}
impl From<CompactContractBytecode> for ContractBytecode {
fn from(c: CompactContractBytecode) -> Self {
let (maybe_bcode, maybe_runtime) = match (c.bytecode, c.deployed_bytecode) {
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
(None, Some(dbcode)) => (None, Some(dbcode.into())),
(Some(bcode), None) => (Some(bcode.into()), None),
(None, None) => (None, None),
};
Self { abi: c.abi, bytecode: maybe_bcode, deployed_bytecode: maybe_runtime }
}
}
/// Minimal representation of a contract with a present abi and bytecode.
///
/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
@ -971,6 +1060,13 @@ impl From<ContractBytecode> for CompactContract {
}
}
impl From<CompactContractBytecode> for CompactContract {
fn from(c: CompactContractBytecode) -> Self {
let c: ContractBytecode = c.into();
c.into()
}
}
impl From<ContractBytecodeSome> for CompactContract {
fn from(c: ContractBytecodeSome) -> Self {
Self {
@ -1183,6 +1279,88 @@ pub struct Bytecode {
pub link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CompactBytecode {
/// The bytecode as a hex string.
pub object: BytecodeObject,
/// The source mapping as a string. See the source mapping definition.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_map: Option<String>,
/// If given, this is an unlinked object.
#[serde(default)]
pub link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
}
impl CompactBytecode {
/// Tries to link the bytecode object with the `file` and `library` name.
/// Replaces all library placeholders with the given address.
///
/// Returns true if the bytecode object is fully linked, false otherwise
/// This is a noop if the bytecode object is already fully linked.
pub fn link(
&mut self,
file: impl AsRef<str>,
library: impl AsRef<str>,
address: Address,
) -> bool {
if !self.object.is_unlinked() {
return true
}
let file = file.as_ref();
let library = library.as_ref();
if let Some((key, mut contracts)) = self.link_references.remove_entry(file) {
if contracts.remove(library).is_some() {
self.object.link(file, library, address);
}
if !contracts.is_empty() {
self.link_references.insert(key, contracts);
}
if self.link_references.is_empty() {
return self.object.resolve().is_some()
}
}
false
}
}
impl From<Bytecode> for CompactBytecode {
fn from(bcode: Bytecode) -> CompactBytecode {
CompactBytecode {
object: bcode.object,
source_map: bcode.source_map,
link_references: bcode.link_references,
}
}
}
impl From<CompactBytecode> for Bytecode {
fn from(bcode: CompactBytecode) -> Bytecode {
Bytecode {
object: bcode.object,
source_map: bcode.source_map,
link_references: bcode.link_references,
function_debug_data: Default::default(),
opcodes: Default::default(),
generated_sources: Default::default(),
}
}
}
impl From<BytecodeObject> for Bytecode {
fn from(object: BytecodeObject) -> Bytecode {
Bytecode {
object,
function_debug_data: Default::default(),
opcodes: Default::default(),
source_map: Default::default(),
generated_sources: Default::default(),
link_references: Default::default(),
}
}
}
impl Bytecode {
/// Returns the parsed source map
///
@ -1453,6 +1631,43 @@ impl DeployedBytecode {
}
}
impl From<Bytecode> for DeployedBytecode {
fn from(bcode: Bytecode) -> DeployedBytecode {
DeployedBytecode { bytecode: Some(bcode), immutable_references: Default::default() }
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CompactDeployedBytecode {
#[serde(flatten)]
pub bytecode: Option<CompactBytecode>,
#[serde(
default,
rename = "immutableReferences",
skip_serializing_if = "::std::collections::BTreeMap::is_empty"
)]
pub immutable_references: BTreeMap<String, Vec<Offsets>>,
}
impl From<DeployedBytecode> for CompactDeployedBytecode {
fn from(bcode: DeployedBytecode) -> CompactDeployedBytecode {
CompactDeployedBytecode {
bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
immutable_references: bcode.immutable_references,
}
}
}
impl From<CompactDeployedBytecode> for DeployedBytecode {
fn from(bcode: CompactDeployedBytecode) -> DeployedBytecode {
DeployedBytecode {
bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
immutable_references: bcode.immutable_references,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct GasEstimates {
pub creation: Creation,

View File

@ -1,5 +1,5 @@
use crate::{
artifacts::{CompactContract, CompactContractRef, Contract, Settings},
artifacts::{CompactContract, CompactContractBytecode, Contract, Settings},
cache::SOLIDITY_FILES_CACHE_FILENAME,
error::{Result, SolcError, SolcIoError},
hh::HardhatArtifact,
@ -503,14 +503,20 @@ pub trait Artifact {
/// Returns the artifact's `Abi` and bytecode
fn into_inner(self) -> (Option<Abi>, Option<Bytes>);
/// Turns the artifact into a container type for abi, bytecode and deployed bytecode
/// Turns the artifact into a container type for abi, compact bytecode and deployed bytecode
fn into_compact_contract(self) -> CompactContract;
/// Turns the artifact into a container type for abi, full bytecode and deployed bytecode
fn into_contract_bytecode(self) -> CompactContractBytecode;
/// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode
fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>);
}
impl<T: Into<CompactContract>> Artifact for T {
impl<T> Artifact for T
where
T: Into<CompactContractBytecode> + Into<CompactContract>,
{
fn into_inner(self) -> (Option<Abi>, Option<Bytes>) {
let artifact = self.into_compact_contract();
(artifact.abi, artifact.bin.and_then(|bin| bin.into_bytes()))
@ -520,6 +526,10 @@ impl<T: Into<CompactContract>> Artifact for T {
self.into()
}
fn into_contract_bytecode(self) -> CompactContractBytecode {
self.into()
}
fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
self.into_compact_contract().into_parts()
}
@ -625,7 +635,7 @@ pub trait ArtifactOutput {
pub struct MinimalCombinedArtifacts;
impl ArtifactOutput for MinimalCombinedArtifacts {
type Artifact = CompactContract;
type Artifact = CompactContractBytecode;
fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()> {
fs::create_dir_all(&layout.artifacts)
@ -643,7 +653,7 @@ impl ArtifactOutput for MinimalCombinedArtifacts {
))
})?;
}
let min = CompactContractRef::from(contract);
let min = CompactContractBytecode::from(contract.clone());
fs::write(&file, serde_json::to_vec_pretty(&min)?)
.map_err(|err| SolcError::io(err, file))?
}
@ -662,7 +672,7 @@ impl ArtifactOutput for MinimalCombinedArtifacts {
pub struct MinimalCombinedArtifactsHardhatFallback;
impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
type Artifact = CompactContract;
type Artifact = CompactContractBytecode;
fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()> {
MinimalCombinedArtifacts::on_output(output, layout)
@ -678,7 +688,7 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
tracing::trace!("Fallback to hardhat artifact deserialization");
let artifact = serde_json::from_str::<HardhatArtifact>(&content)?;
tracing::trace!("successfully deserialized hardhat artifact");
Ok(artifact.into_compact_contract())
Ok(artifact.into_contract_bytecode())
}
}

View File

@ -1,7 +1,10 @@
//! Hardhat support
use crate::{
artifacts::{BytecodeObject, CompactContract, Contract, Offsets},
artifacts::{
Bytecode, BytecodeObject, CompactContract, CompactContractBytecode, Contract,
ContractBytecode, DeployedBytecode, Offsets,
},
error::{Result, SolcError},
ArtifactOutput, CompilerOutput, ProjectPathsConfig,
};
@ -49,6 +52,32 @@ impl From<HardhatArtifact> for CompactContract {
}
}
impl From<HardhatArtifact> for ContractBytecode {
fn from(artifact: HardhatArtifact) -> Self {
let bytecode: Option<Bytecode> = artifact.bytecode.as_ref().map(|t| {
let mut bcode: Bytecode = t.clone().into();
bcode.link_references = artifact.link_references.clone();
bcode
});
let deployed_bytecode: Option<DeployedBytecode> = artifact.bytecode.as_ref().map(|t| {
let mut bcode: Bytecode = t.clone().into();
bcode.link_references = artifact.deployed_link_references.clone();
bcode.into()
});
ContractBytecode { abi: Some(artifact.abi), bytecode, deployed_bytecode }
}
}
impl From<HardhatArtifact> for CompactContractBytecode {
fn from(artifact: HardhatArtifact) -> Self {
let c: ContractBytecode = artifact.into();
c.into()
}
}
/// Hardhat style artifacts handler
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct HardhatArtifacts;

View File

@ -918,11 +918,11 @@ impl<T: ArtifactOutput + 'static> ProjectCompileOutput<T> {
///
/// ```no_run
/// use std::collections::BTreeMap;
/// use ethers_solc::artifacts::CompactContract;
/// use ethers_solc::artifacts::CompactContractBytecode;
/// use ethers_solc::Project;
///
/// let project = Project::builder().build().unwrap();
/// let contracts: BTreeMap<String, CompactContract> = project.compile().unwrap().into_artifacts().collect();
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_artifacts().collect();
/// ```
pub fn into_artifacts(mut self) -> Box<dyn Iterator<Item = (String, T::Artifact)>> {
let artifacts = self.artifacts.into_iter().filter_map(|(path, art)| {