feat: support parsing bytecode from evm object (#2024)

This commit is contained in:
Matthias Seitz 2023-01-07 15:22:07 +01:00 committed by GitHub
parent 2aa7bc3b99
commit 01d3e4310f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 9 deletions

View File

@ -117,6 +117,26 @@ pub enum JsonAbi {
Array(RawAbi),
}
// === impl JsonAbi ===
impl JsonAbi {
/// Returns the bytecode object
pub fn bytecode(&self) -> Option<Bytes> {
match self {
JsonAbi::Object(abi) => abi.bytecode.clone(),
JsonAbi::Array(_) => None,
}
}
/// Returns the deployed bytecode object
pub fn deployed_bytecode(&self) -> Option<Bytes> {
match self {
JsonAbi::Object(abi) => abi.deployed_bytecode.clone(),
JsonAbi::Array(_) => None,
}
}
}
fn deserialize_abi_array<'de, D>(deserializer: D) -> Result<RawAbi, D::Error>
where
D: Deserializer<'de>,
@ -128,6 +148,7 @@ where
pub struct AbiObject {
pub abi: RawAbi,
pub bytecode: Option<Bytes>,
pub deployed_bytecode: Option<Bytes>,
}
struct AbiObjectVisitor;
@ -145,6 +166,7 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
{
let mut abi = None;
let mut bytecode = None;
let mut deployed_bytecode = None;
#[derive(Deserialize)]
#[serde(untagged)]
@ -153,6 +175,28 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
Bytes(Bytes),
}
impl Bytecode {
fn into_bytes(self) -> Option<Bytes> {
let bytecode = match self {
Bytecode::Object { object } => object,
Bytecode::Bytes(bytes) => bytes,
};
if bytecode.is_empty() {
None
} else {
Some(bytecode)
}
}
}
/// represents nested bytecode objects of the `evm` value
#[derive(Deserialize)]
struct EvmObj {
bytecode: Option<Bytecode>,
#[serde(rename = "deployedBytecode")]
deployed_bytecode: Option<Bytecode>,
}
struct DeserializeBytes(Bytes);
impl<'de> Deserialize<'de> for DeserializeBytes {
@ -169,15 +213,18 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
"abi" => {
abi = Some(RawAbi(map.next_value::<Vec<Item>>()?));
}
"evm" => {
if let Ok(evm) = map.next_value::<EvmObj>() {
bytecode = evm.bytecode.and_then(|b| b.into_bytes());
deployed_bytecode = evm.deployed_bytecode.and_then(|b| b.into_bytes())
}
}
"bytecode" | "byteCode" => {
bytecode = map
.next_value::<Bytecode>()
.ok()
.map(|obj| match obj {
Bytecode::Object { object } => object,
Bytecode::Bytes(bytes) => bytes,
})
.filter(|bytecode| !bytecode.0.is_empty());
bytecode = map.next_value::<Bytecode>().ok().and_then(|b| b.into_bytes());
}
"deployedbytecode" | "deployedBytecode" => {
deployed_bytecode =
map.next_value::<Bytecode>().ok().and_then(|b| b.into_bytes());
}
"bin" => {
bytecode = map
@ -186,6 +233,13 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
.map(|b| b.0)
.filter(|b| !b.0.is_empty());
}
"runtimebin" | "runtimeBin" => {
deployed_bytecode = map
.next_value::<DeserializeBytes>()
.ok()
.map(|b| b.0)
.filter(|b| !b.0.is_empty());
}
_ => {
map.next_value::<serde::de::IgnoredAny>()?;
}
@ -193,7 +247,7 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
}
let abi = abi.ok_or_else(|| serde::de::Error::missing_field("abi"))?;
Ok(AbiObject { abi, bytecode })
Ok(AbiObject { abi, bytecode, deployed_bytecode })
}
}
@ -316,4 +370,18 @@ mod tests {
}
}
}
#[test]
fn can_parse_deployed_bytecode() {
let artifact = include_str!("../../testdata/solc-obj.json");
match serde_json::from_str::<JsonAbi>(artifact).unwrap() {
JsonAbi::Object(abi) => {
assert!(abi.bytecode.is_some());
assert!(abi.deployed_bytecode.is_some());
}
_ => {
panic!("expected abi object")
}
}
}
}

1
ethers-core/testdata/solc-obj.json vendored Normal file

File diff suppressed because one or more lines are too long