Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

write CompactContractBytecode instead of CompactContract #833

Merged
merged 2 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 215 additions & 0 deletions ethers-solc/src/artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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,
Expand Down
24 changes: 17 additions & 7 deletions ethers-solc/src/config.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -448,14 +448,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()))
Expand All @@ -465,6 +471,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()
}
Expand Down Expand Up @@ -570,7 +580,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)
Expand All @@ -588,7 +598,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))?
}
Expand All @@ -607,7 +617,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)
Expand All @@ -623,7 +633,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())
}
}

Expand Down
31 changes: 30 additions & 1 deletion ethers-solc/src/hh.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand Down Expand Up @@ -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;
Expand Down