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

fix(solc): consistent serde for linked and unlinked bytecode #948

Merged
merged 1 commit into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 10 additions & 2 deletions ethers-solc/src/artifacts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@ pub enum BytecodeObject {
#[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),
}

Expand All @@ -1612,6 +1613,13 @@ impl BytecodeObject {
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> {
Expand Down Expand Up @@ -1715,10 +1723,10 @@ impl BytecodeObject {
}
}

// Returns a not deployable bytecode by default as "0x"
// Returns a not deployable bytecode by default as empty
impl Default for BytecodeObject {
fn default() -> Self {
BytecodeObject::Unlinked("0x".to_string())
BytecodeObject::Unlinked("".to_string())
}
}

Expand Down
28 changes: 28 additions & 0 deletions ethers-solc/src/artifacts/serde_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,34 @@ pub mod json_string_opt {
}
}

/// serde support for string
pub mod string_bytes {
use serde::{Deserialize, Deserializer, Serializer};

pub fn serialize<S>(value: &String, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if value.starts_with("0x") {
serializer.serialize_str(value.as_str())
} else {
serializer.serialize_str(&format!("0x{}", value))
}
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
if let Some(rem) = value.strip_prefix("0x") {
Ok(rem.to_string())
} else {
Ok(value)
}
}
}

pub mod display_from_str_opt {
use serde::{de, Deserialize, Deserializer, Serializer};
use std::{fmt, str::FromStr};
Expand Down
41 changes: 38 additions & 3 deletions ethers-solc/tests/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ fn can_detect_type_error() {
fn can_compile_single_files() {
let tmp = TempProject::dapptools().unwrap();

let foo = tmp
let f = tmp
.add_contract(
"examples/Foo",
r#"
Expand All @@ -470,7 +470,7 @@ fn can_compile_single_files() {
)
.unwrap();

let compiled = tmp.project().compile_file(foo.clone()).unwrap();
let compiled = tmp.project().compile_file(f.clone()).unwrap();
assert!(!compiled.has_compiler_errors());
assert!(compiled.find("Foo").is_some());

Expand All @@ -485,8 +485,43 @@ fn can_compile_single_files() {
)
.unwrap();

let compiled = tmp.project().compile_files(vec![foo, bar]).unwrap();
let compiled = tmp.project().compile_files(vec![f, bar]).unwrap();
assert!(!compiled.has_compiler_errors());
assert!(compiled.find("Foo").is_some());
assert!(compiled.find("Bar").is_some());
}

#[test]
fn consistent_bytecode() {
let tmp = TempProject::dapptools().unwrap();

tmp.add_source(
"LinkTest",
r#"
// SPDX-License-Identifier: MIT
library LibTest {
function foobar(uint256 a) public view returns (uint256) {
return a * 100;
}
}
contract LinkTest {
function foo() public returns (uint256) {
return LibTest.foobar(1);
}
}
"#,
)
.unwrap();

let compiled = tmp.compile().unwrap();
assert!(!compiled.has_compiler_errors());

let contract = compiled.find("LinkTest").unwrap();
let bytecode = &contract.bytecode.as_ref().unwrap().object;
assert!(bytecode.is_unlinked());
let s = bytecode.as_str().unwrap();
assert!(!s.starts_with("0x"));

let s = serde_json::to_string(&bytecode).unwrap();
assert_eq!(bytecode.clone(), serde_json::from_str(&s).unwrap());
}