fix(etherscan): source code serde (#1962)
This commit is contained in:
parent
5008006767
commit
1baf88138f
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
source_tree::{SourceTree, SourceTreeEntry},
|
source_tree::{SourceTree, SourceTreeEntry},
|
||||||
utils::{deserialize_address_opt, deserialize_stringified_source_code},
|
utils::{deserialize_address_opt, deserialize_source_code},
|
||||||
Client, EtherscanError, Response, Result,
|
Client, EtherscanError, Response, Result,
|
||||||
};
|
};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
|
@ -107,7 +107,7 @@ impl SourceCodeMetadata {
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
/// Includes metadata for compiler settings and language.
|
/// Includes metadata for compiler settings and language.
|
||||||
#[serde(deserialize_with = "deserialize_stringified_source_code")]
|
#[serde(deserialize_with = "deserialize_source_code")]
|
||||||
pub source_code: SourceCodeMetadata,
|
pub source_code: SourceCodeMetadata,
|
||||||
|
|
||||||
/// The ABI of the contract.
|
/// The ABI of the contract.
|
||||||
|
@ -148,7 +148,11 @@ pub struct Metadata {
|
||||||
pub proxy: u64,
|
pub proxy: u64,
|
||||||
|
|
||||||
/// If this contract is a proxy, the address of its implementation.
|
/// If this contract is a proxy, the address of its implementation.
|
||||||
#[serde(deserialize_with = "deserialize_address_opt")]
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
deserialize_with = "deserialize_address_opt"
|
||||||
|
)]
|
||||||
pub implementation: Option<Address>,
|
pub implementation: Option<Address>,
|
||||||
|
|
||||||
/// The swarm source of the contract.
|
/// The swarm source of the contract.
|
||||||
|
|
|
@ -36,20 +36,35 @@ pub fn deserialize_address_opt<'de, D: Deserializer<'de>>(
|
||||||
|
|
||||||
/// Deserializes as JSON:
|
/// Deserializes as JSON:
|
||||||
///
|
///
|
||||||
/// `{ "SourceCode": "{{ .. }}", ..}`
|
/// Object: `{ "SourceCode": { language: "Solidity", .. }, ..}`
|
||||||
///
|
///
|
||||||
/// or
|
/// or
|
||||||
///
|
///
|
||||||
/// `{ "SourceCode": "..", .. }`
|
/// Stringified JSON: `{ "SourceCode": "{{\r\n \"language\": \"Solidity\", ..}}", ..}`
|
||||||
pub fn deserialize_stringified_source_code<'de, D: Deserializer<'de>>(
|
///
|
||||||
|
/// or
|
||||||
|
///
|
||||||
|
/// Normal source code: `{ "SourceCode": "// SPDX-License-Identifier: ...", .. }`
|
||||||
|
pub fn deserialize_source_code<'de, D: Deserializer<'de>>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> std::result::Result<SourceCodeMetadata, D::Error> {
|
) -> std::result::Result<SourceCodeMetadata, D::Error> {
|
||||||
let s = String::deserialize(deserializer)?;
|
#[derive(Deserialize)]
|
||||||
if s.starts_with("{{") && s.ends_with("}}") {
|
#[serde(untagged)]
|
||||||
let s = &s[1..s.len() - 1];
|
enum SourceCode {
|
||||||
serde_json::from_str(s).map_err(serde::de::Error::custom)
|
String(String), // this must come first
|
||||||
} else {
|
Obj(SourceCodeMetadata),
|
||||||
Ok(SourceCodeMetadata::SourceCode(s))
|
}
|
||||||
|
let s = SourceCode::deserialize(deserializer)?;
|
||||||
|
match s {
|
||||||
|
SourceCode::String(s) => {
|
||||||
|
if s.starts_with("{{") && s.ends_with("}}") {
|
||||||
|
let s = &s[1..s.len() - 1];
|
||||||
|
serde_json::from_str(s).map_err(serde::de::Error::custom)
|
||||||
|
} else {
|
||||||
|
Ok(SourceCodeMetadata::SourceCode(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourceCode::Obj(obj) => Ok(obj),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,17 +123,29 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_deserialize_stringified_source_code() {
|
fn can_deserialize_source_code() {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Test {
|
struct Test {
|
||||||
#[serde(deserialize_with = "deserialize_stringified_source_code")]
|
#[serde(deserialize_with = "deserialize_source_code")]
|
||||||
source_code: SourceCodeMetadata,
|
source_code: SourceCodeMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source code text";
|
let src = "source code text";
|
||||||
|
|
||||||
|
// Normal JSON
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
"source_code": "{{ \"language\": \"Solidity\", \"sources\": {\"Contract\": { \"content\": \"source code text\" } } }}"
|
"source_code": { "language": "Solidity", "sources": { "Contract": { "content": "source code text" } } }
|
||||||
|
}"#;
|
||||||
|
let de: Test = serde_json::from_str(json).unwrap();
|
||||||
|
assert!(matches!(de.source_code.language().unwrap(), SourceCodeLanguage::Solidity));
|
||||||
|
assert_eq!(de.source_code.sources().len(), 1);
|
||||||
|
assert_eq!(de.source_code.sources().get("Contract").unwrap().content, src);
|
||||||
|
#[cfg(feature = "ethers-solc")]
|
||||||
|
assert!(matches!(de.source_code.settings().unwrap(), None));
|
||||||
|
|
||||||
|
// Stringified JSON
|
||||||
|
let json = r#"{
|
||||||
|
"source_code": "{{ \"language\": \"Solidity\", \"sources\": { \"Contract\": { \"content\": \"source code text\" } } }}"
|
||||||
}"#;
|
}"#;
|
||||||
let de: Test = serde_json::from_str(json).unwrap();
|
let de: Test = serde_json::from_str(json).unwrap();
|
||||||
assert!(matches!(de.source_code.language().unwrap(), SourceCodeLanguage::Solidity));
|
assert!(matches!(de.source_code.language().unwrap(), SourceCodeLanguage::Solidity));
|
||||||
|
|
Loading…
Reference in New Issue