style: split artifacts mod and create contract and bytecode mods (#1052)
* style: split artifacts mod and create contract and bytecode mods * chore: rustfmt
This commit is contained in:
parent
b53aa500fe
commit
65b1751cf8
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{
|
artifacts::{
|
||||||
|
bytecode::{CompactBytecode, CompactDeployedBytecode},
|
||||||
|
contract::{CompactContract, CompactContractBytecode, Contract},
|
||||||
output_selection::{ContractOutputSelection, EvmOutputSelection, EwasmOutputSelection},
|
output_selection::{ContractOutputSelection, EvmOutputSelection, EwasmOutputSelection},
|
||||||
CompactBytecode, CompactContract, CompactContractBytecode, CompactDeployedBytecode,
|
|
||||||
CompactEvm, DevDoc, Ewasm, GasEstimates, LosslessAbi, Metadata, Offsets, Settings,
|
CompactEvm, DevDoc, Ewasm, GasEstimates, LosslessAbi, Metadata, Offsets, Settings,
|
||||||
StorageLayout, UserDoc,
|
StorageLayout, UserDoc,
|
||||||
},
|
},
|
||||||
ArtifactOutput, Contract, SolcConfig, SolcError,
|
ArtifactOutput, SolcConfig, SolcError,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::BTreeMap, fs, path::Path};
|
use std::{collections::BTreeMap, fs, path::Path};
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
//! Output artifact handling
|
//! Output artifact handling
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{CompactContract, CompactContractBytecode, Contract, FileToContractsMap},
|
artifacts::FileToContractsMap, contracts::VersionedContracts, error::Result, utils,
|
||||||
contracts::VersionedContracts,
|
HardhatArtifact, ProjectPathsConfig, SolcError,
|
||||||
error::Result,
|
|
||||||
utils, HardhatArtifact, ProjectPathsConfig, SolcError,
|
|
||||||
};
|
};
|
||||||
use ethers_core::{abi::Abi, types::Bytes};
|
use ethers_core::{abi::Abi, types::Bytes};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
@ -16,6 +14,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod configurable;
|
mod configurable;
|
||||||
|
use crate::artifacts::contract::{CompactContract, CompactContractBytecode, Contract};
|
||||||
pub use configurable::*;
|
pub use configurable::*;
|
||||||
|
|
||||||
/// Represents unique artifact metadata for identifying artifacts on output
|
/// Represents unique artifact metadata for identifying artifacts on output
|
||||||
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
//! Bytecode related types
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
artifacts::{serde_helpers, FunctionDebugData, GeneratedSource, Offsets},
|
||||||
|
sourcemap::{self, SourceMap, SyntaxError},
|
||||||
|
utils,
|
||||||
|
};
|
||||||
|
use ethers_core::{abi::Address, types::Bytes};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Bytecode {
|
||||||
|
/// Debugging information at function level
|
||||||
|
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
||||||
|
pub function_debug_data: BTreeMap<String, FunctionDebugData>,
|
||||||
|
/// The bytecode as a hex string.
|
||||||
|
pub object: BytecodeObject,
|
||||||
|
/// Opcodes list (string)
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub opcodes: Option<String>,
|
||||||
|
/// The source mapping as a string. See the source mapping definition.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub source_map: Option<String>,
|
||||||
|
/// Array of sources generated by the compiler. Currently only contains a
|
||||||
|
/// single Yul file.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub generated_sources: Vec<GeneratedSource>,
|
||||||
|
/// If given, this is an unlinked object.
|
||||||
|
#[serde(default)]
|
||||||
|
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
|
||||||
|
///
|
||||||
|
/// See also https://docs.soliditylang.org/en/v0.8.10/internals/source_mappings.html
|
||||||
|
pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
|
||||||
|
self.source_map.as_ref().map(|map| sourcemap::parse(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `Bytecode::link` but with fully qualified name (`file.sol:Math`)
|
||||||
|
pub fn link_fully_qualified(&mut self, name: impl AsRef<str>, addr: Address) -> bool {
|
||||||
|
if let Some((file, lib)) = name.as_ref().split_once(':') {
|
||||||
|
self.link(file, lib, addr)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Links the bytecode object with all provided `(file, lib, addr)`
|
||||||
|
pub fn link_all<I, S, T>(&mut self, libs: I) -> bool
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (S, T, Address)>,
|
||||||
|
S: AsRef<str>,
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
for (file, lib, addr) in libs.into_iter() {
|
||||||
|
if self.link(file, lib, addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Links the bytecode object with all provided `(fully_qualified, addr)`
|
||||||
|
pub fn link_all_fully_qualified<I, S>(&mut self, libs: I) -> bool
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (S, Address)>,
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
for (name, addr) in libs.into_iter() {
|
||||||
|
if self.link_fully_qualified(name, addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the bytecode of a contracts that might be not fully linked yet.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum BytecodeObject {
|
||||||
|
/// Fully linked bytecode object
|
||||||
|
#[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
|
||||||
|
Bytecode(Bytes),
|
||||||
|
/// Bytecode as hex string that's not fully linked yet and contains library placeholders
|
||||||
|
#[serde(with = "serde_helpers::string_bytes")]
|
||||||
|
Unlinked(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytecodeObject {
|
||||||
|
/// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty
|
||||||
|
pub fn into_bytes(self) -> Option<Bytes> {
|
||||||
|
match self {
|
||||||
|
BytecodeObject::Bytecode(bytes) => Some(bytes),
|
||||||
|
BytecodeObject::Unlinked(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the underlying `Bytes` if the object is a valid bytecode, and not
|
||||||
|
/// empty
|
||||||
|
pub fn as_bytes(&self) -> Option<&Bytes> {
|
||||||
|
match self {
|
||||||
|
BytecodeObject::Bytecode(bytes) => Some(bytes),
|
||||||
|
BytecodeObject::Unlinked(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Returns a reference to the underlying `String` if the object is unlinked
|
||||||
|
pub fn as_str(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
BytecodeObject::Bytecode(_) => None,
|
||||||
|
BytecodeObject::Unlinked(s) => Some(s.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the unlinked `String` if the object is unlinked or empty
|
||||||
|
pub fn into_unlinked(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
BytecodeObject::Bytecode(_) => None,
|
||||||
|
BytecodeObject::Unlinked(code) => Some(code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this object is still unlinked
|
||||||
|
pub fn is_unlinked(&self) -> bool {
|
||||||
|
matches!(self, BytecodeObject::Unlinked(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this object a valid bytecode
|
||||||
|
pub fn is_bytecode(&self) -> bool {
|
||||||
|
matches!(self, BytecodeObject::Bytecode(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the object is a valid bytecode and not empty.
|
||||||
|
/// Returns false the object is a valid but empty bytecode or unlinked.
|
||||||
|
pub fn is_non_empty_bytecode(&self) -> bool {
|
||||||
|
self.as_bytes().map(|c| !c.0.is_empty()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to resolve the unlinked string object a valid bytecode object in place
|
||||||
|
///
|
||||||
|
/// Returns the string if it is a valid
|
||||||
|
pub fn resolve(&mut self) -> Option<&Bytes> {
|
||||||
|
if let BytecodeObject::Unlinked(unlinked) = self {
|
||||||
|
if let Ok(linked) = hex::decode(unlinked) {
|
||||||
|
*self = BytecodeObject::Bytecode(linked.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Link using the fully qualified name of a library
|
||||||
|
/// The fully qualified library name is the path of its source file and the library name
|
||||||
|
/// separated by `:` like `file.sol:Math`
|
||||||
|
///
|
||||||
|
/// This will replace all occurrences of the library placeholder with the given address.
|
||||||
|
///
|
||||||
|
/// See also: https://docs.soliditylang.org/en/develop/using-the-compiler.html#library-linking
|
||||||
|
pub fn link_fully_qualified(&mut self, name: impl AsRef<str>, addr: Address) -> &mut Self {
|
||||||
|
if let BytecodeObject::Unlinked(ref mut unlinked) = self {
|
||||||
|
let name = name.as_ref();
|
||||||
|
let place_holder = utils::library_hash_placeholder(name);
|
||||||
|
// the address as hex without prefix
|
||||||
|
let hex_addr = hex::encode(addr);
|
||||||
|
|
||||||
|
// the library placeholder used to be the fully qualified name of the library instead of
|
||||||
|
// the hash. This is also still supported by `solc` so we handle this as well
|
||||||
|
let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name);
|
||||||
|
|
||||||
|
*unlinked = unlinked
|
||||||
|
.replace(&format!("__{}__", fully_qualified_placeholder), &hex_addr)
|
||||||
|
.replace(&format!("__{}__", place_holder), &hex_addr)
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Link using the `file` and `library` names as fully qualified name `<file>:<library>`
|
||||||
|
/// See `BytecodeObject::link_fully_qualified`
|
||||||
|
pub fn link(
|
||||||
|
&mut self,
|
||||||
|
file: impl AsRef<str>,
|
||||||
|
library: impl AsRef<str>,
|
||||||
|
addr: Address,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.link_fully_qualified(format!("{}:{}", file.as_ref(), library.as_ref(),), addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Links the bytecode object with all provided `(file, lib, addr)`
|
||||||
|
pub fn link_all<I, S, T>(&mut self, libs: I) -> &mut Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (S, T, Address)>,
|
||||||
|
S: AsRef<str>,
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
for (file, lib, addr) in libs.into_iter() {
|
||||||
|
self.link(file, lib, addr);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the bytecode contains a matching placeholder using the qualified name
|
||||||
|
pub fn contains_fully_qualified_placeholder(&self, name: impl AsRef<str>) -> bool {
|
||||||
|
if let BytecodeObject::Unlinked(unlinked) = self {
|
||||||
|
let name = name.as_ref();
|
||||||
|
unlinked.contains(&utils::library_hash_placeholder(name)) ||
|
||||||
|
unlinked.contains(&utils::library_fully_qualified_placeholder(name))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the bytecode contains a matching placeholder
|
||||||
|
pub fn contains_placeholder(&self, file: impl AsRef<str>, library: impl AsRef<str>) -> bool {
|
||||||
|
self.contains_fully_qualified_placeholder(format!("{}:{}", file.as_ref(), library.as_ref()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a not deployable bytecode by default as empty
|
||||||
|
impl Default for BytecodeObject {
|
||||||
|
fn default() -> Self {
|
||||||
|
BytecodeObject::Unlinked("".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for BytecodeObject {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
BytecodeObject::Bytecode(code) => code.as_ref(),
|
||||||
|
BytecodeObject::Unlinked(code) => code.as_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct DeployedBytecode {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub bytecode: Option<Bytecode>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
rename = "immutableReferences",
|
||||||
|
skip_serializing_if = "::std::collections::BTreeMap::is_empty"
|
||||||
|
)]
|
||||||
|
pub immutable_references: BTreeMap<String, Vec<Offsets>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeployedBytecode {
|
||||||
|
/// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty
|
||||||
|
pub fn into_bytes(self) -> Option<Bytes> {
|
||||||
|
self.bytecode?.object.into_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,478 @@
|
||||||
|
//! Contract related types
|
||||||
|
|
||||||
|
use crate::artifacts::{
|
||||||
|
bytecode::{
|
||||||
|
Bytecode, BytecodeObject, CompactBytecode, CompactDeployedBytecode, DeployedBytecode,
|
||||||
|
},
|
||||||
|
serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, Metadata, Offsets, StorageLayout, UserDoc,
|
||||||
|
};
|
||||||
|
use ethers_core::{abi::Contract as Abi, types::Bytes};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::BTreeMap, convert::TryFrom};
|
||||||
|
|
||||||
|
/// Represents a compiled solidity contract
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Contract {
|
||||||
|
/// The Ethereum Contract Metadata.
|
||||||
|
/// See https://docs.soliditylang.org/en/develop/metadata.html
|
||||||
|
pub abi: Option<LosslessAbi>,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
with = "serde_helpers::json_string_opt"
|
||||||
|
)]
|
||||||
|
pub metadata: Option<Metadata>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub userdoc: UserDoc,
|
||||||
|
#[serde(default)]
|
||||||
|
pub devdoc: DevDoc,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ir: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
|
||||||
|
pub storage_layout: StorageLayout,
|
||||||
|
/// EVM-related outputs
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub evm: Option<Evm>,
|
||||||
|
/// Ewasm related outputs
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ewasm: Option<Ewasm>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ir_optimized: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 ContractBytecode {
|
||||||
|
/// 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<Bytecode>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub deployed_bytecode: Option<DeployedBytecode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContractBytecode {
|
||||||
|
/// Returns the `ContractBytecodeSome` if all fields are `Some`
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any of the fields equal `None`
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_solc::Project;
|
||||||
|
/// use ethers_solc::artifacts::*;
|
||||||
|
/// # fn demo(project: Project) {
|
||||||
|
/// let mut output = project.compile().unwrap().output();
|
||||||
|
/// let contract: ContractBytecode = output.remove("Greeter").unwrap().into();
|
||||||
|
/// let contract = contract.unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn unwrap(self) -> ContractBytecodeSome {
|
||||||
|
ContractBytecodeSome {
|
||||||
|
abi: self.abi.unwrap(),
|
||||||
|
bytecode: self.bytecode.unwrap(),
|
||||||
|
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 {
|
||||||
|
fn from(c: Contract) -> Self {
|
||||||
|
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
|
||||||
|
(evm.bytecode, evm.deployed_bytecode)
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { abi: c.abi.map(Into::into), bytecode, deployed_bytecode }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, Default, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
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 evm = evm.into_compact();
|
||||||
|
(evm.bytecode, evm.deployed_bytecode)
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { abi: c.abi.map(Into::into), 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
|
||||||
|
/// `Bytecode` object.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct ContractBytecodeSome {
|
||||||
|
pub abi: Abi,
|
||||||
|
pub bytecode: Bytecode,
|
||||||
|
pub deployed_bytecode: DeployedBytecode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ContractBytecode> for ContractBytecodeSome {
|
||||||
|
type Error = ContractBytecode;
|
||||||
|
|
||||||
|
fn try_from(value: ContractBytecode) -> Result<Self, Self::Error> {
|
||||||
|
if value.abi.is_none() || value.bytecode.is_none() || value.deployed_bytecode.is_none() {
|
||||||
|
return Err(value)
|
||||||
|
}
|
||||||
|
Ok(value.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimal representation of a contract's artifact with a present abi and bytecode.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct CompactContractSome {
|
||||||
|
/// 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: Abi,
|
||||||
|
pub bin: BytecodeObject,
|
||||||
|
#[serde(rename = "bin-runtime")]
|
||||||
|
pub bin_runtime: BytecodeObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CompactContract> for CompactContractSome {
|
||||||
|
type Error = CompactContract;
|
||||||
|
|
||||||
|
fn try_from(value: CompactContract) -> Result<Self, Self::Error> {
|
||||||
|
if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
|
||||||
|
return Err(value)
|
||||||
|
}
|
||||||
|
Ok(value.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The general purpose minimal representation of a contract's abi with bytecode
|
||||||
|
/// Unlike `CompactContractSome` all fields are optional so that every possible compiler output can
|
||||||
|
/// be represented by it
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
pub struct CompactContract {
|
||||||
|
/// 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 bin: Option<BytecodeObject>,
|
||||||
|
#[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bin_runtime: Option<BytecodeObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompactContract {
|
||||||
|
/// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode
|
||||||
|
pub fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
|
||||||
|
(
|
||||||
|
self.abi,
|
||||||
|
self.bin.and_then(|bin| bin.into_bytes()),
|
||||||
|
self.bin_runtime.and_then(|bin| bin.into_bytes()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the individual parts of this contract.
|
||||||
|
///
|
||||||
|
/// If the values are `None`, then `Default` is returned.
|
||||||
|
pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) {
|
||||||
|
(
|
||||||
|
self.abi.unwrap_or_default(),
|
||||||
|
self.bin.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
|
||||||
|
self.bin_runtime.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `CompactContractSome` if all fields are `Some`
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any of the fields euqal `None`
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_solc::Project;
|
||||||
|
/// use ethers_solc::artifacts::*;
|
||||||
|
/// # fn demo(project: Project) {
|
||||||
|
/// let mut output = project.compile().unwrap().output();
|
||||||
|
/// let contract: CompactContract = output.remove("Greeter").unwrap().into();
|
||||||
|
/// let contract = contract.unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn unwrap(self) -> CompactContractSome {
|
||||||
|
CompactContractSome {
|
||||||
|
abi: self.abi.unwrap(),
|
||||||
|
bin: self.bin.unwrap(),
|
||||||
|
bin_runtime: self.bin_runtime.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `CompactContractSome` if any if the field equals `None` the `Default` value is
|
||||||
|
/// returned
|
||||||
|
///
|
||||||
|
/// Unlike `unwrap`, this function does _not_ panic
|
||||||
|
pub fn unwrap_or_default(self) -> CompactContractSome {
|
||||||
|
CompactContractSome {
|
||||||
|
abi: self.abi.unwrap_or_default(),
|
||||||
|
bin: self.bin.unwrap_or_default(),
|
||||||
|
bin_runtime: self.bin_runtime.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Value> for CompactContract {
|
||||||
|
fn from(mut val: serde_json::Value) -> Self {
|
||||||
|
if let Some(map) = val.as_object_mut() {
|
||||||
|
let abi = map.remove("abi").and_then(|val| serde_json::from_value(val).ok());
|
||||||
|
let bin = map.remove("bin").and_then(|val| serde_json::from_value(val).ok());
|
||||||
|
let bin_runtime =
|
||||||
|
map.remove("bin-runtime").and_then(|val| serde_json::from_value(val).ok());
|
||||||
|
Self { abi, bin, bin_runtime }
|
||||||
|
} else {
|
||||||
|
CompactContract::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Value> for CompactContractBytecode {
|
||||||
|
fn from(val: serde_json::Value) -> Self {
|
||||||
|
serde_json::from_value(val).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ContractBytecode> for CompactContract {
|
||||||
|
fn from(c: ContractBytecode) -> Self {
|
||||||
|
let ContractBytecode { abi, bytecode, deployed_bytecode } = c;
|
||||||
|
Self {
|
||||||
|
abi,
|
||||||
|
bin: bytecode.map(|c| c.object),
|
||||||
|
bin_runtime: deployed_bytecode
|
||||||
|
.and_then(|deployed| deployed.bytecode.map(|code| code.object)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
abi: Some(c.abi),
|
||||||
|
bin: Some(c.bytecode.object),
|
||||||
|
bin_runtime: c.deployed_bytecode.bytecode.map(|code| code.object),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Contract> for CompactContract {
|
||||||
|
fn from(c: Contract) -> Self {
|
||||||
|
ContractBytecode::from(c).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompactContractSome> for CompactContract {
|
||||||
|
fn from(c: CompactContractSome) -> Self {
|
||||||
|
Self { abi: Some(c.abi), bin: Some(c.bin), bin_runtime: Some(c.bin_runtime) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<CompactContractRef<'a>> for CompactContract {
|
||||||
|
fn from(c: CompactContractRef<'a>) -> Self {
|
||||||
|
Self { abi: c.abi.cloned(), bin: c.bin.cloned(), bin_runtime: c.bin_runtime.cloned() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<CompactContractRefSome<'a>> for CompactContract {
|
||||||
|
fn from(c: CompactContractRefSome<'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
abi: Some(c.abi.clone()),
|
||||||
|
bin: Some(c.bin.clone()),
|
||||||
|
bin_runtime: Some(c.bin_runtime.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimal representation of a contract with a present abi and bytecode that borrows.
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize)]
|
||||||
|
pub struct CompactContractRefSome<'a> {
|
||||||
|
pub abi: &'a Abi,
|
||||||
|
pub bin: &'a BytecodeObject,
|
||||||
|
#[serde(rename = "bin-runtime")]
|
||||||
|
pub bin_runtime: &'a BytecodeObject,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CompactContractRefSome<'a> {
|
||||||
|
/// Returns the individual parts of this contract.
|
||||||
|
///
|
||||||
|
/// If the values are `None`, then `Default` is returned.
|
||||||
|
pub fn into_parts(self) -> (Abi, Bytes, Bytes) {
|
||||||
|
CompactContract::from(self).into_parts_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<CompactContractRef<'a>> for CompactContractRefSome<'a> {
|
||||||
|
type Error = CompactContractRef<'a>;
|
||||||
|
|
||||||
|
fn try_from(value: CompactContractRef<'a>) -> Result<Self, Self::Error> {
|
||||||
|
if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
|
||||||
|
return Err(value)
|
||||||
|
}
|
||||||
|
Ok(value.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper type to serialize while borrowing from `Contract`
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize)]
|
||||||
|
pub struct CompactContractRef<'a> {
|
||||||
|
pub abi: Option<&'a Abi>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bin: Option<&'a BytecodeObject>,
|
||||||
|
#[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bin_runtime: Option<&'a BytecodeObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CompactContractRef<'a> {
|
||||||
|
/// Clones the referenced values and returns as tuples
|
||||||
|
pub fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
|
||||||
|
CompactContract::from(self).into_parts()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the individual parts of this contract.
|
||||||
|
///
|
||||||
|
/// If the values are `None`, then `Default` is returned.
|
||||||
|
pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) {
|
||||||
|
CompactContract::from(self).into_parts_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytecode(&self) -> Option<&Bytes> {
|
||||||
|
self.bin.as_ref().and_then(|bin| bin.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_bytecode(&self) -> Option<&Bytes> {
|
||||||
|
self.bin_runtime.as_ref().and_then(|bin| bin.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `CompactContractRefSome` if all fields are `Some`
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any of the fields equal `None`
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_solc::Project;
|
||||||
|
/// use ethers_solc::artifacts::*;
|
||||||
|
/// # fn demo(project: Project) {
|
||||||
|
/// let output = project.compile().unwrap().output();
|
||||||
|
/// let contract = output.find("Greeter").unwrap();
|
||||||
|
/// let contract = contract.unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn unwrap(self) -> CompactContractRefSome<'a> {
|
||||||
|
CompactContractRefSome {
|
||||||
|
abi: self.abi.unwrap(),
|
||||||
|
bin: self.bin.unwrap(),
|
||||||
|
bin_runtime: self.bin_runtime.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Contract> for CompactContractRef<'a> {
|
||||||
|
fn from(c: &'a Contract) -> Self {
|
||||||
|
let (bin, bin_runtime) = if let Some(ref evm) = c.evm {
|
||||||
|
(
|
||||||
|
evm.bytecode.as_ref().map(|c| &c.object),
|
||||||
|
evm.deployed_bytecode
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|deployed| deployed.bytecode.as_ref().map(|evm| &evm.object)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { abi: c.abi.as_ref().map(|abi| &abi.abi), bin, bin_runtime }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +1,30 @@
|
||||||
//! Solc artifact types
|
//! Solc artifact types
|
||||||
use ethers_core::{abi::Abi, types::Bytes};
|
use ethers_core::abi::Abi;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use md5::Digest;
|
use md5::Digest;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
convert::TryFrom,
|
|
||||||
fmt, fs,
|
fmt, fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{compile::*, error::SolcIoError, remappings::Remapping, utils};
|
||||||
compile::*,
|
|
||||||
error::SolcIoError,
|
|
||||||
remappings::Remapping,
|
|
||||||
sourcemap::{self, SourceMap, SyntaxError},
|
|
||||||
utils,
|
|
||||||
};
|
|
||||||
use ethers_core::abi::Address;
|
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
pub mod bytecode;
|
||||||
|
pub mod contract;
|
||||||
pub mod output_selection;
|
pub mod output_selection;
|
||||||
pub mod serde_helpers;
|
pub mod serde_helpers;
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::output_selection::{ContractOutputSelection, OutputSelection},
|
artifacts::output_selection::{ContractOutputSelection, OutputSelection},
|
||||||
filter::FilteredSources,
|
filter::FilteredSources,
|
||||||
};
|
};
|
||||||
|
pub use bytecode::*;
|
||||||
|
pub use contract::*;
|
||||||
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
||||||
|
|
||||||
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
||||||
|
@ -809,37 +806,6 @@ impl OutputContracts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a compiled solidity contract
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Contract {
|
|
||||||
/// The Ethereum Contract Metadata.
|
|
||||||
/// See https://docs.soliditylang.org/en/develop/metadata.html
|
|
||||||
pub abi: Option<LosslessAbi>,
|
|
||||||
#[serde(
|
|
||||||
default,
|
|
||||||
skip_serializing_if = "Option::is_none",
|
|
||||||
with = "serde_helpers::json_string_opt"
|
|
||||||
)]
|
|
||||||
pub metadata: Option<Metadata>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub userdoc: UserDoc,
|
|
||||||
#[serde(default)]
|
|
||||||
pub devdoc: DevDoc,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub ir: Option<String>,
|
|
||||||
#[serde(default, skip_serializing_if = "StorageLayout::is_empty")]
|
|
||||||
pub storage_layout: StorageLayout,
|
|
||||||
/// EVM-related outputs
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub evm: Option<Evm>,
|
|
||||||
/// Ewasm related outputs
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub ewasm: Option<Ewasm>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub ir_optimized: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper type that ensures lossless (de)serialisation unlike [`ethabi::Contract`] which omits
|
/// A helper type that ensures lossless (de)serialisation unlike [`ethabi::Contract`] which omits
|
||||||
/// some information of (nested) components in a serde roundtrip. This is a problem for
|
/// some information of (nested) components in a serde roundtrip. This is a problem for
|
||||||
/// abienconderv2 structs because `ethabi::Contract`'s representation of those are [`ethabi::Param`]
|
/// abienconderv2 structs because `ethabi::Contract`'s representation of those are [`ethabi::Param`]
|
||||||
|
@ -882,442 +848,6 @@ impl<'de> Deserialize<'de> for LosslessAbi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 ContractBytecode {
|
|
||||||
/// 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<Bytecode>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub deployed_bytecode: Option<DeployedBytecode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContractBytecode {
|
|
||||||
/// Returns the `ContractBytecodeSome` if all fields are `Some`
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if any of the fields euqal `None`
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ethers_solc::Project;
|
|
||||||
/// use ethers_solc::artifacts::*;
|
|
||||||
/// # fn demo(project: Project) {
|
|
||||||
/// let mut output = project.compile().unwrap().output();
|
|
||||||
/// let contract: ContractBytecode = output.remove("Greeter").unwrap().into();
|
|
||||||
/// let contract = contract.unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn unwrap(self) -> ContractBytecodeSome {
|
|
||||||
ContractBytecodeSome {
|
|
||||||
abi: self.abi.unwrap(),
|
|
||||||
bytecode: self.bytecode.unwrap(),
|
|
||||||
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 {
|
|
||||||
fn from(c: Contract) -> Self {
|
|
||||||
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
|
|
||||||
(evm.bytecode, evm.deployed_bytecode)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { abi: c.abi.map(Into::into), bytecode, deployed_bytecode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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, Default, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
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 evm = evm.into_compact();
|
|
||||||
(evm.bytecode, evm.deployed_bytecode)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { abi: c.abi.map(Into::into), 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
|
|
||||||
/// `Bytecode` object.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct ContractBytecodeSome {
|
|
||||||
pub abi: Abi,
|
|
||||||
pub bytecode: Bytecode,
|
|
||||||
pub deployed_bytecode: DeployedBytecode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ContractBytecode> for ContractBytecodeSome {
|
|
||||||
type Error = ContractBytecode;
|
|
||||||
|
|
||||||
fn try_from(value: ContractBytecode) -> Result<Self, Self::Error> {
|
|
||||||
if value.abi.is_none() || value.bytecode.is_none() || value.deployed_bytecode.is_none() {
|
|
||||||
return Err(value)
|
|
||||||
}
|
|
||||||
Ok(value.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal representation of a contract's artifact with a present abi and bytecode.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct CompactContractSome {
|
|
||||||
/// 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: Abi,
|
|
||||||
pub bin: BytecodeObject,
|
|
||||||
#[serde(rename = "bin-runtime")]
|
|
||||||
pub bin_runtime: BytecodeObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<CompactContract> for CompactContractSome {
|
|
||||||
type Error = CompactContract;
|
|
||||||
|
|
||||||
fn try_from(value: CompactContract) -> Result<Self, Self::Error> {
|
|
||||||
if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
|
|
||||||
return Err(value)
|
|
||||||
}
|
|
||||||
Ok(value.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The general purpose minimal representation of a contract's abi with bytecode
|
|
||||||
/// Unlike `CompactContractSome` all fields are optional so that every possible compiler output can
|
|
||||||
/// be represented by it
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
pub struct CompactContract {
|
|
||||||
/// 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 bin: Option<BytecodeObject>,
|
|
||||||
#[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub bin_runtime: Option<BytecodeObject>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompactContract {
|
|
||||||
/// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode
|
|
||||||
pub fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
|
|
||||||
(
|
|
||||||
self.abi,
|
|
||||||
self.bin.and_then(|bin| bin.into_bytes()),
|
|
||||||
self.bin_runtime.and_then(|bin| bin.into_bytes()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the individual parts of this contract.
|
|
||||||
///
|
|
||||||
/// If the values are `None`, then `Default` is returned.
|
|
||||||
pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) {
|
|
||||||
(
|
|
||||||
self.abi.unwrap_or_default(),
|
|
||||||
self.bin.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
|
|
||||||
self.bin_runtime.and_then(|bin| bin.into_bytes()).unwrap_or_default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `CompactContractSome` if all fields are `Some`
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if any of the fields euqal `None`
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ethers_solc::Project;
|
|
||||||
/// use ethers_solc::artifacts::*;
|
|
||||||
/// # fn demo(project: Project) {
|
|
||||||
/// let mut output = project.compile().unwrap().output();
|
|
||||||
/// let contract: CompactContract = output.remove("Greeter").unwrap().into();
|
|
||||||
/// let contract = contract.unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn unwrap(self) -> CompactContractSome {
|
|
||||||
CompactContractSome {
|
|
||||||
abi: self.abi.unwrap(),
|
|
||||||
bin: self.bin.unwrap(),
|
|
||||||
bin_runtime: self.bin_runtime.unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `CompactContractSome` if any if the field equals `None` the `Default` value is
|
|
||||||
/// returned
|
|
||||||
///
|
|
||||||
/// Unlike `unwrap`, this function does _not_ panic
|
|
||||||
pub fn unwrap_or_default(self) -> CompactContractSome {
|
|
||||||
CompactContractSome {
|
|
||||||
abi: self.abi.unwrap_or_default(),
|
|
||||||
bin: self.bin.unwrap_or_default(),
|
|
||||||
bin_runtime: self.bin_runtime.unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::Value> for CompactContract {
|
|
||||||
fn from(mut val: serde_json::Value) -> Self {
|
|
||||||
if let Some(map) = val.as_object_mut() {
|
|
||||||
let abi = map.remove("abi").and_then(|val| serde_json::from_value(val).ok());
|
|
||||||
let bin = map.remove("bin").and_then(|val| serde_json::from_value(val).ok());
|
|
||||||
let bin_runtime =
|
|
||||||
map.remove("bin-runtime").and_then(|val| serde_json::from_value(val).ok());
|
|
||||||
Self { abi, bin, bin_runtime }
|
|
||||||
} else {
|
|
||||||
CompactContract::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::Value> for CompactContractBytecode {
|
|
||||||
fn from(val: serde_json::Value) -> Self {
|
|
||||||
serde_json::from_value(val).unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ContractBytecode> for CompactContract {
|
|
||||||
fn from(c: ContractBytecode) -> Self {
|
|
||||||
let ContractBytecode { abi, bytecode, deployed_bytecode } = c;
|
|
||||||
Self {
|
|
||||||
abi,
|
|
||||||
bin: bytecode.map(|c| c.object),
|
|
||||||
bin_runtime: deployed_bytecode
|
|
||||||
.and_then(|deployed| deployed.bytecode.map(|code| code.object)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
abi: Some(c.abi),
|
|
||||||
bin: Some(c.bytecode.object),
|
|
||||||
bin_runtime: c.deployed_bytecode.bytecode.map(|code| code.object),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Contract> for CompactContract {
|
|
||||||
fn from(c: Contract) -> Self {
|
|
||||||
ContractBytecode::from(c).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CompactContractSome> for CompactContract {
|
|
||||||
fn from(c: CompactContractSome) -> Self {
|
|
||||||
Self { abi: Some(c.abi), bin: Some(c.bin), bin_runtime: Some(c.bin_runtime) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<CompactContractRef<'a>> for CompactContract {
|
|
||||||
fn from(c: CompactContractRef<'a>) -> Self {
|
|
||||||
Self { abi: c.abi.cloned(), bin: c.bin.cloned(), bin_runtime: c.bin_runtime.cloned() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<CompactContractRefSome<'a>> for CompactContract {
|
|
||||||
fn from(c: CompactContractRefSome<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
abi: Some(c.abi.clone()),
|
|
||||||
bin: Some(c.bin.clone()),
|
|
||||||
bin_runtime: Some(c.bin_runtime.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal representation of a contract with a present abi and bytecode that borrows.
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize)]
|
|
||||||
pub struct CompactContractRefSome<'a> {
|
|
||||||
pub abi: &'a Abi,
|
|
||||||
pub bin: &'a BytecodeObject,
|
|
||||||
#[serde(rename = "bin-runtime")]
|
|
||||||
pub bin_runtime: &'a BytecodeObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> CompactContractRefSome<'a> {
|
|
||||||
/// Returns the individual parts of this contract.
|
|
||||||
///
|
|
||||||
/// If the values are `None`, then `Default` is returned.
|
|
||||||
pub fn into_parts(self) -> (Abi, Bytes, Bytes) {
|
|
||||||
CompactContract::from(self).into_parts_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TryFrom<CompactContractRef<'a>> for CompactContractRefSome<'a> {
|
|
||||||
type Error = CompactContractRef<'a>;
|
|
||||||
|
|
||||||
fn try_from(value: CompactContractRef<'a>) -> Result<Self, Self::Error> {
|
|
||||||
if value.abi.is_none() || value.bin.is_none() || value.bin_runtime.is_none() {
|
|
||||||
return Err(value)
|
|
||||||
}
|
|
||||||
Ok(value.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper type to serialize while borrowing from `Contract`
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize)]
|
|
||||||
pub struct CompactContractRef<'a> {
|
|
||||||
pub abi: Option<&'a Abi>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub bin: Option<&'a BytecodeObject>,
|
|
||||||
#[serde(default, rename = "bin-runtime", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub bin_runtime: Option<&'a BytecodeObject>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> CompactContractRef<'a> {
|
|
||||||
/// Clones the referenced values and returns as tuples
|
|
||||||
pub fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
|
|
||||||
CompactContract::from(self).into_parts()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the individual parts of this contract.
|
|
||||||
///
|
|
||||||
/// If the values are `None`, then `Default` is returned.
|
|
||||||
pub fn into_parts_or_default(self) -> (Abi, Bytes, Bytes) {
|
|
||||||
CompactContract::from(self).into_parts_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytecode(&self) -> Option<&Bytes> {
|
|
||||||
self.bin.as_ref().and_then(|bin| bin.as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runtime_bytecode(&self) -> Option<&Bytes> {
|
|
||||||
self.bin_runtime.as_ref().and_then(|bin| bin.as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `CompactContractRefSome` if all fields are `Some`
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if any of the fields equal `None`
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ethers_solc::Project;
|
|
||||||
/// use ethers_solc::artifacts::*;
|
|
||||||
/// # fn demo(project: Project) {
|
|
||||||
/// let output = project.compile().unwrap().output();
|
|
||||||
/// let contract = output.find("Greeter").unwrap();
|
|
||||||
/// let contract = contract.unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub fn unwrap(self) -> CompactContractRefSome<'a> {
|
|
||||||
CompactContractRefSome {
|
|
||||||
abi: self.abi.unwrap(),
|
|
||||||
bin: self.bin.unwrap(),
|
|
||||||
bin_runtime: self.bin_runtime.unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Contract> for CompactContractRef<'a> {
|
|
||||||
fn from(c: &'a Contract) -> Self {
|
|
||||||
let (bin, bin_runtime) = if let Some(ref evm) = c.evm {
|
|
||||||
(
|
|
||||||
evm.bytecode.as_ref().map(|c| &c.object),
|
|
||||||
evm.deployed_bytecode
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|deployed| deployed.bytecode.as_ref().map(|evm| &evm.object)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { abi: c.abi.as_ref().map(|abi| &abi.abi), bin, bin_runtime }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
pub struct UserDoc {
|
pub struct UserDoc {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -1425,344 +955,6 @@ pub(crate) struct CompactEvm {
|
||||||
pub gas_estimates: Option<GasEstimates>,
|
pub gas_estimates: Option<GasEstimates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Bytecode {
|
|
||||||
/// Debugging information at function level
|
|
||||||
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
|
||||||
pub function_debug_data: BTreeMap<String, FunctionDebugData>,
|
|
||||||
/// The bytecode as a hex string.
|
|
||||||
pub object: BytecodeObject,
|
|
||||||
/// Opcodes list (string)
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub opcodes: Option<String>,
|
|
||||||
/// The source mapping as a string. See the source mapping definition.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub source_map: Option<String>,
|
|
||||||
/// Array of sources generated by the compiler. Currently only contains a
|
|
||||||
/// single Yul file.
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub generated_sources: Vec<GeneratedSource>,
|
|
||||||
/// If given, this is an unlinked object.
|
|
||||||
#[serde(default)]
|
|
||||||
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
|
|
||||||
///
|
|
||||||
/// See also https://docs.soliditylang.org/en/v0.8.10/internals/source_mappings.html
|
|
||||||
pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
|
|
||||||
self.source_map.as_ref().map(|map| sourcemap::parse(map))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `Bytecode::link` but with fully qualified name (`file.sol:Math`)
|
|
||||||
pub fn link_fully_qualified(&mut self, name: impl AsRef<str>, addr: Address) -> bool {
|
|
||||||
if let Some((file, lib)) = name.as_ref().split_once(':') {
|
|
||||||
self.link(file, lib, addr)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links the bytecode object with all provided `(file, lib, addr)`
|
|
||||||
pub fn link_all<I, S, T>(&mut self, libs: I) -> bool
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (S, T, Address)>,
|
|
||||||
S: AsRef<str>,
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
for (file, lib, addr) in libs.into_iter() {
|
|
||||||
if self.link(file, lib, addr) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links the bytecode object with all provided `(fully_qualified, addr)`
|
|
||||||
pub fn link_all_fully_qualified<I, S>(&mut self, libs: I) -> bool
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (S, Address)>,
|
|
||||||
S: AsRef<str>,
|
|
||||||
{
|
|
||||||
for (name, addr) in libs.into_iter() {
|
|
||||||
if self.link_fully_qualified(name, addr) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the bytecode of a contracts that might be not fully linked yet.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum BytecodeObject {
|
|
||||||
/// Fully linked bytecode object
|
|
||||||
#[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
|
|
||||||
Bytecode(Bytes),
|
|
||||||
/// Bytecode as hex string that's not fully linked yet and contains library placeholders
|
|
||||||
#[serde(with = "serde_helpers::string_bytes")]
|
|
||||||
Unlinked(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytecodeObject {
|
|
||||||
/// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty
|
|
||||||
pub fn into_bytes(self) -> Option<Bytes> {
|
|
||||||
match self {
|
|
||||||
BytecodeObject::Bytecode(bytes) => Some(bytes),
|
|
||||||
BytecodeObject::Unlinked(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the underlying `Bytes` if the object is a valid bytecode, and not
|
|
||||||
/// empty
|
|
||||||
pub fn as_bytes(&self) -> Option<&Bytes> {
|
|
||||||
match self {
|
|
||||||
BytecodeObject::Bytecode(bytes) => Some(bytes),
|
|
||||||
BytecodeObject::Unlinked(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns a reference to the underlying `String` if the object is unlinked
|
|
||||||
pub fn as_str(&self) -> Option<&str> {
|
|
||||||
match self {
|
|
||||||
BytecodeObject::Bytecode(_) => None,
|
|
||||||
BytecodeObject::Unlinked(s) => Some(s.as_str()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the unlinked `String` if the object is unlinked or empty
|
|
||||||
pub fn into_unlinked(self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
BytecodeObject::Bytecode(_) => None,
|
|
||||||
BytecodeObject::Unlinked(code) => Some(code),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this object is still unlinked
|
|
||||||
pub fn is_unlinked(&self) -> bool {
|
|
||||||
matches!(self, BytecodeObject::Unlinked(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this object a valid bytecode
|
|
||||||
pub fn is_bytecode(&self) -> bool {
|
|
||||||
matches!(self, BytecodeObject::Bytecode(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the object is a valid bytecode and not empty.
|
|
||||||
/// Returns false the object is a valid but empty bytecode or unlinked.
|
|
||||||
pub fn is_non_empty_bytecode(&self) -> bool {
|
|
||||||
self.as_bytes().map(|c| !c.0.is_empty()).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to resolve the unlinked string object a valid bytecode object in place
|
|
||||||
///
|
|
||||||
/// Returns the string if it is a valid
|
|
||||||
pub fn resolve(&mut self) -> Option<&Bytes> {
|
|
||||||
if let BytecodeObject::Unlinked(unlinked) = self {
|
|
||||||
if let Ok(linked) = hex::decode(unlinked) {
|
|
||||||
*self = BytecodeObject::Bytecode(linked.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.as_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Link using the fully qualified name of a library
|
|
||||||
/// The fully qualified library name is the path of its source file and the library name
|
|
||||||
/// separated by `:` like `file.sol:Math`
|
|
||||||
///
|
|
||||||
/// This will replace all occurrences of the library placeholder with the given address.
|
|
||||||
///
|
|
||||||
/// See also: https://docs.soliditylang.org/en/develop/using-the-compiler.html#library-linking
|
|
||||||
pub fn link_fully_qualified(&mut self, name: impl AsRef<str>, addr: Address) -> &mut Self {
|
|
||||||
if let BytecodeObject::Unlinked(ref mut unlinked) = self {
|
|
||||||
let name = name.as_ref();
|
|
||||||
let place_holder = utils::library_hash_placeholder(name);
|
|
||||||
// the address as hex without prefix
|
|
||||||
let hex_addr = hex::encode(addr);
|
|
||||||
|
|
||||||
// the library placeholder used to be the fully qualified name of the library instead of
|
|
||||||
// the hash. This is also still supported by `solc` so we handle this as well
|
|
||||||
let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name);
|
|
||||||
|
|
||||||
*unlinked = unlinked
|
|
||||||
.replace(&format!("__{}__", fully_qualified_placeholder), &hex_addr)
|
|
||||||
.replace(&format!("__{}__", place_holder), &hex_addr)
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Link using the `file` and `library` names as fully qualified name `<file>:<library>`
|
|
||||||
/// See `BytecodeObject::link_fully_qualified`
|
|
||||||
pub fn link(
|
|
||||||
&mut self,
|
|
||||||
file: impl AsRef<str>,
|
|
||||||
library: impl AsRef<str>,
|
|
||||||
addr: Address,
|
|
||||||
) -> &mut Self {
|
|
||||||
self.link_fully_qualified(format!("{}:{}", file.as_ref(), library.as_ref(),), addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links the bytecode object with all provided `(file, lib, addr)`
|
|
||||||
pub fn link_all<I, S, T>(&mut self, libs: I) -> &mut Self
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (S, T, Address)>,
|
|
||||||
S: AsRef<str>,
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
for (file, lib, addr) in libs.into_iter() {
|
|
||||||
self.link(file, lib, addr);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the bytecode contains a matching placeholder using the qualified name
|
|
||||||
pub fn contains_fully_qualified_placeholder(&self, name: impl AsRef<str>) -> bool {
|
|
||||||
if let BytecodeObject::Unlinked(unlinked) = self {
|
|
||||||
let name = name.as_ref();
|
|
||||||
unlinked.contains(&utils::library_hash_placeholder(name)) ||
|
|
||||||
unlinked.contains(&utils::library_fully_qualified_placeholder(name))
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the bytecode contains a matching placeholder
|
|
||||||
pub fn contains_placeholder(&self, file: impl AsRef<str>, library: impl AsRef<str>) -> bool {
|
|
||||||
self.contains_fully_qualified_placeholder(format!("{}:{}", file.as_ref(), library.as_ref()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a not deployable bytecode by default as empty
|
|
||||||
impl Default for BytecodeObject {
|
|
||||||
fn default() -> Self {
|
|
||||||
BytecodeObject::Unlinked("".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for BytecodeObject {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
match self {
|
|
||||||
BytecodeObject::Bytecode(code) => code.as_ref(),
|
|
||||||
BytecodeObject::Unlinked(code) => code.as_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct FunctionDebugData {
|
pub struct FunctionDebugData {
|
||||||
|
@ -1789,62 +981,6 @@ pub struct Offsets {
|
||||||
pub length: u32,
|
pub length: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
|
||||||
pub struct DeployedBytecode {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub bytecode: Option<Bytecode>,
|
|
||||||
#[serde(
|
|
||||||
default,
|
|
||||||
rename = "immutableReferences",
|
|
||||||
skip_serializing_if = "::std::collections::BTreeMap::is_empty"
|
|
||||||
)]
|
|
||||||
pub immutable_references: BTreeMap<String, Vec<Offsets>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeployedBytecode {
|
|
||||||
/// Returns the underlying `Bytes` if the object is a valid bytecode, and not empty
|
|
||||||
pub fn into_bytes(self) -> Option<Bytes> {
|
|
||||||
self.bytecode?.object.into_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
pub struct GasEstimates {
|
pub struct GasEstimates {
|
||||||
pub creation: Creation,
|
pub creation: Creation,
|
||||||
|
@ -2073,6 +1209,7 @@ impl SourceFiles {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::AggregatedCompilerOutput;
|
use crate::AggregatedCompilerOutput;
|
||||||
|
use ethers_core::types::Address;
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -181,7 +181,7 @@ impl SolFilesCache {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// fn t() {
|
/// fn t() {
|
||||||
/// use ethers_solc::artifacts::CompactContract;
|
/// use ethers_solc::artifacts::contract::CompactContract;
|
||||||
/// use ethers_solc::cache::SolFilesCache;
|
/// use ethers_solc::cache::SolFilesCache;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
|
@ -234,7 +234,7 @@ impl SolFilesCache {
|
||||||
/// fn t() {
|
/// fn t() {
|
||||||
/// use ethers_solc::cache::SolFilesCache;
|
/// use ethers_solc::cache::SolFilesCache;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
/// use ethers_solc::artifacts::CompactContract;
|
/// use ethers_solc::artifacts::contract::CompactContract;
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
/// let cache = SolFilesCache::read_joined(&project.paths).unwrap();
|
/// let cache = SolFilesCache::read_joined(&project.paths).unwrap();
|
||||||
|
@ -267,7 +267,7 @@ impl SolFilesCache {
|
||||||
/// ```
|
/// ```
|
||||||
/// use ethers_solc::cache::SolFilesCache;
|
/// use ethers_solc::cache::SolFilesCache;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
/// use ethers_solc::artifacts::contract::CompactContractBytecode;
|
||||||
/// # fn t() {
|
/// # fn t() {
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
/// let cache = SolFilesCache::read_joined(&project.paths).unwrap();
|
/// let cache = SolFilesCache::read_joined(&project.paths).unwrap();
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::artifacts::{CompactContractRef, Contract, FileToContractsMap};
|
use crate::artifacts::{
|
||||||
|
contract::{CompactContractRef, Contract},
|
||||||
|
FileToContractsMap,
|
||||||
|
};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -112,6 +115,7 @@ impl VersionedContracts {
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::collections::BTreeMap;
|
/// use std::collections::BTreeMap;
|
||||||
/// use ethers_solc::{ artifacts::*, Artifact };
|
/// use ethers_solc::{ artifacts::*, Artifact };
|
||||||
|
/// use ethers_solc::artifacts::contract::CompactContractSome;
|
||||||
/// # fn demo(contracts: OutputContracts) {
|
/// # fn demo(contracts: OutputContracts) {
|
||||||
/// let contracts: BTreeMap<String, CompactContractSome> = contracts
|
/// let contracts: BTreeMap<String, CompactContractSome> = contracts
|
||||||
/// .into_contracts()
|
/// .into_contracts()
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{
|
artifacts::{
|
||||||
CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles,
|
contract::{CompactContractBytecode, CompactContractRef, Contract},
|
||||||
|
Error, SourceFile, SourceFiles,
|
||||||
},
|
},
|
||||||
contracts::{VersionedContract, VersionedContracts},
|
contracts::{VersionedContract, VersionedContracts},
|
||||||
ArtifactId, ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts,
|
ArtifactId, ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts,
|
||||||
|
@ -77,7 +78,7 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
/// Make all artifact files relative tot the project's root directory
|
/// Make all artifact files relative tot the project's root directory
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
/// use ethers_solc::artifacts::contract::CompactContractBytecode;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
|
@ -93,7 +94,7 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
/// Get the (merged) solc compiler output
|
/// Get the (merged) solc compiler output
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::collections::btree_map::BTreeMap;
|
/// use std::collections::btree_map::BTreeMap;
|
||||||
/// use ethers_solc::artifacts::Contract;
|
/// use ethers_solc::artifacts::contract::Contract;
|
||||||
/// use ethers_solc::Project;
|
/// use ethers_solc::Project;
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
|
@ -182,7 +183,7 @@ impl ProjectCompileOutput<ConfigurableArtifacts> {
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::collections::btree_map::BTreeMap;
|
/// use std::collections::btree_map::BTreeMap;
|
||||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
/// use ethers_solc::artifacts::contract::CompactContractBytecode;
|
||||||
/// use ethers_solc::{ArtifactId, Project};
|
/// use ethers_solc::{ArtifactId, Project};
|
||||||
///
|
///
|
||||||
/// let project = Project::builder().build().unwrap();
|
/// let project = Project::builder().build().unwrap();
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{
|
artifacts::{
|
||||||
Bytecode, BytecodeObject, CompactContract, CompactContractBytecode, Contract,
|
bytecode::{Bytecode, BytecodeObject, DeployedBytecode},
|
||||||
ContractBytecode, DeployedBytecode, LosslessAbi, Offsets,
|
contract::{CompactContract, CompactContractBytecode, Contract, ContractBytecode},
|
||||||
|
LosslessAbi, Offsets,
|
||||||
},
|
},
|
||||||
ArtifactOutput,
|
ArtifactOutput,
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,11 +32,12 @@ pub mod utils;
|
||||||
pub use filter::{FileFilter, TestFileFilter};
|
pub use filter::{FileFilter, TestFileFilter};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{Contract, Sources},
|
artifacts::Sources,
|
||||||
cache::SolFilesCache,
|
cache::SolFilesCache,
|
||||||
contracts::VersionedContracts,
|
contracts::VersionedContracts,
|
||||||
error::{SolcError, SolcIoError},
|
error::{SolcError, SolcIoError},
|
||||||
};
|
};
|
||||||
|
use artifacts::contract::Contract;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
Loading…
Reference in New Issue