feat(solc): emit additional raw metadata field (#1365)
* feat(solc): emit additional raw metadata field * chore: update CHANGELOG * test: updata test * fix(solc): proper metadata deserialization
This commit is contained in:
parent
c96f6ba616
commit
980649060a
|
@ -99,6 +99,8 @@
|
|||
|
||||
### Unreleased
|
||||
|
||||
- Add `rawMetadata:String` field to configurable contract output
|
||||
[#1365](https://github.com/gakonst/ethers-rs/pull/1365)
|
||||
- Use relative source paths and `solc --base-path`
|
||||
[#1317](https://github.com/gakonst/ethers-rs/pull/1317)
|
||||
- Save cache entry objects with relative paths
|
||||
|
|
|
@ -9,7 +9,8 @@ use crate::{
|
|||
EwasmOutputSelection,
|
||||
},
|
||||
Ast, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, GasEstimates,
|
||||
GeneratedSource, LosslessAbi, Metadata, Offsets, Settings, StorageLayout, UserDoc,
|
||||
GeneratedSource, LosslessAbi, LosslessMetadata, Metadata, Offsets, Settings, StorageLayout,
|
||||
UserDoc,
|
||||
},
|
||||
sources::VersionedSourceFile,
|
||||
ArtifactOutput, SolcConfig, SolcError, SourceFile,
|
||||
|
@ -41,6 +42,8 @@ pub struct ConfigurableContractArtifact {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub gas_estimates: Option<GasEstimates>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub raw_metadata: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<Metadata>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub storage_layout: Option<StorageLayout>,
|
||||
|
@ -232,6 +235,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
|
|||
) -> Self::Artifact {
|
||||
let mut artifact_userdoc = None;
|
||||
let mut artifact_devdoc = None;
|
||||
let mut artifact_raw_metadata = None;
|
||||
let mut artifact_metadata = None;
|
||||
let mut artifact_ir = None;
|
||||
let mut artifact_ir_optimized = None;
|
||||
|
@ -258,7 +262,10 @@ impl ArtifactOutput for ConfigurableArtifacts {
|
|||
} = contract;
|
||||
|
||||
if self.additional_values.metadata {
|
||||
artifact_metadata = metadata;
|
||||
if let Some(LosslessMetadata { raw_metadata, metadata }) = metadata {
|
||||
artifact_raw_metadata = Some(raw_metadata);
|
||||
artifact_metadata = Some(metadata);
|
||||
}
|
||||
}
|
||||
if self.additional_values.userdoc {
|
||||
artifact_userdoc = Some(userdoc);
|
||||
|
@ -318,6 +325,7 @@ impl ArtifactOutput for ConfigurableArtifacts {
|
|||
function_debug_data: artifact_function_debug_data,
|
||||
method_identifiers: artifact_method_identifiers,
|
||||
gas_estimates: artifact_gas_estimates,
|
||||
raw_metadata: artifact_raw_metadata,
|
||||
metadata: artifact_metadata,
|
||||
storage_layout: artifact_storage_layout,
|
||||
userdoc: artifact_userdoc,
|
||||
|
@ -564,7 +572,7 @@ impl ExtraOutputFiles {
|
|||
if self.metadata {
|
||||
if let Some(ref metadata) = contract.metadata {
|
||||
let file = file.with_extension("metadata.json");
|
||||
fs::write(&file, serde_json::to_string_pretty(metadata)?)
|
||||
fs::write(&file, serde_json::to_string_pretty(&metadata.raw_json()?)?)
|
||||
.map_err(|err| SolcError::io(err, file))?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ use crate::artifacts::{
|
|||
bytecode::{
|
||||
Bytecode, BytecodeObject, CompactBytecode, CompactDeployedBytecode, DeployedBytecode,
|
||||
},
|
||||
serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, Metadata, Offsets, StorageLayout, UserDoc,
|
||||
serde_helpers, DevDoc, Evm, Ewasm, LosslessAbi, LosslessMetadata, Offsets, StorageLayout,
|
||||
UserDoc,
|
||||
};
|
||||
use ethers_core::{abi::Contract as Abi, types::Bytes};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -22,7 +23,7 @@ pub struct Contract {
|
|||
skip_serializing_if = "Option::is_none",
|
||||
with = "serde_helpers::json_string_opt"
|
||||
)]
|
||||
pub metadata: Option<Metadata>,
|
||||
pub metadata: Option<LosslessMetadata>,
|
||||
#[serde(default)]
|
||||
pub userdoc: UserDoc,
|
||||
#[serde(default)]
|
||||
|
|
|
@ -856,6 +856,61 @@ pub struct Metadata {
|
|||
pub version: i64,
|
||||
}
|
||||
|
||||
/// A helper type that ensures lossless (de)serialisation so we can preserve the exact String
|
||||
/// metadata value that's being hashed by solc
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LosslessMetadata {
|
||||
/// The complete abi as json value
|
||||
pub raw_metadata: String,
|
||||
/// The deserialised metadata of `raw_metadata`
|
||||
pub metadata: Metadata,
|
||||
}
|
||||
|
||||
// === impl LosslessMetadata ===
|
||||
|
||||
impl LosslessMetadata {
|
||||
/// Returns the whole string raw metadata as `serde_json::Value`
|
||||
pub fn raw_json(&self) -> serde_json::Result<serde_json::Value> {
|
||||
serde_json::from_str(&self.raw_metadata)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for LosslessMetadata {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.raw_metadata.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LosslessMetadata {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct LosslessMetadataVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for LosslessMetadataVisitor {
|
||||
type Value = LosslessMetadata;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "metadata string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
let raw_metadata = value.to_string();
|
||||
let metadata = serde_json::from_str(value).map_err(serde::de::Error::custom)?;
|
||||
Ok(LosslessMetadata { raw_metadata, metadata })
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(LosslessMetadataVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiler settings
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct MetadataSettings {
|
||||
|
|
|
@ -66,8 +66,8 @@ pub mod json_string_opt {
|
|||
if s.is_empty() {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
serde_json::from_str(&s).map_err(de::Error::custom).map(Some)
|
||||
let value = serde_json::Value::String(s);
|
||||
serde_json::from_value(value).map_err(de::Error::custom).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ fn can_compile_configured() {
|
|||
let compiled = project.compile().unwrap();
|
||||
let artifact = compiled.find("Dapp").unwrap();
|
||||
assert!(artifact.metadata.is_some());
|
||||
assert!(artifact.raw_metadata.is_some());
|
||||
assert!(artifact.ir.is_some());
|
||||
assert!(artifact.ir_optimized.is_some());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue