Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deserialization failure when fetching contract source_code from blockscout #22

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: 8 additions & 4 deletions src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
serde_helpers::deserialize_stringified_u64,
serde_helpers::{deserialize_stringified_bool_or_u64, deserialize_stringified_u64},
source_tree::{SourceTree, SourceTreeEntry},
utils::{deserialize_address_opt, deserialize_source_code},
Client, EtherscanError, Response, Result,
Expand Down Expand Up @@ -128,14 +128,15 @@ pub struct Metadata {
pub compiler_version: String,

/// Whether the optimizer was used. This value should only be 0 or 1.
#[serde(deserialize_with = "deserialize_stringified_u64")]
#[serde(deserialize_with = "deserialize_stringified_bool_or_u64")]
pub optimization_used: u64,

/// The number of optimizations performed.
#[serde(deserialize_with = "deserialize_stringified_u64")]
#[serde(deserialize_with = "deserialize_stringified_u64", alias = "OptimizationRuns")]
pub runs: u64,

/// The constructor arguments the contract was deployed with.
#[serde(default)]
pub constructor_arguments: Bytes,

/// The version of the EVM the contract was deployed in. Can be either a variant of EvmVersion
Expand All @@ -144,13 +145,15 @@ pub struct Metadata {
pub evm_version: String,

// ?
#[serde(default)]
pub library: String,

/// The license of the contract.
#[serde(default)]
pub license_type: String,

/// Whether this contract is a proxy. This value should only be 0 or 1.
#[serde(deserialize_with = "deserialize_stringified_u64")]
#[serde(deserialize_with = "deserialize_stringified_bool_or_u64", alias = "IsProxy")]
pub proxy: u64,

/// If this contract is a proxy, the address of its implementation.
Expand All @@ -162,6 +165,7 @@ pub struct Metadata {
pub implementation: Option<Address>,

/// The swarm source of the contract.
#[serde(default)]
pub swarm_source: String,
}

Expand Down
26 changes: 26 additions & 0 deletions src/serde_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,32 @@ impl TryFrom<StringifiedNumeric> for U64 {
}
}

#[derive(Deserialize)]
#[serde(untagged)]
enum BoolOrU64 {
#[serde(deserialize_with = "deserialize_stringified_u64")]
U64(u64),
Bool(String),
}

/// Supports parsing either a u64 or a boolean (which will then be converted to u64)
/// Implemented to binary fields such as "OptimizationUsed" which are formatted either as 0/1 or
/// "true/"false" by different block explorers (e.g. etherscan vs blockscout)
pub fn deserialize_stringified_bool_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let num = BoolOrU64::deserialize(deserializer)?;
match num {
BoolOrU64::Bool(b) => {
let b = b.parse::<bool>().map_err(serde::de::Error::custom)?;
let u = if b { 1 } else { 0 };
Ok(u)
}
BoolOrU64::U64(u) => Ok(u),
}
}

/// Supports parsing u64
///
/// See <https://github.com/gakonst/ethers-rs/issues/1507>
Expand Down
23 changes: 23 additions & 0 deletions tests/it/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ async fn can_fetch_contract_abi() {
.await;
}

#[tokio::test]
#[serial]
async fn can_fetch_contract_source_code_from_blockscout() {
let client = Client::builder()
.with_url("https://eth.blockscout.com")
.unwrap()
.with_api_url("https://eth.blockscout.com/api")
.unwrap()
.with_api_key("test")
.build()
.unwrap();
let meta = client
.contract_source_code("0x00000000219ab540356cBB839Cbe05303d7705Fa".parse().unwrap())
.await
.unwrap();

assert_eq!(meta.items.len(), 1);
let item = &meta.items[0];
assert!(matches!(item.source_code, SourceCodeMetadata::SourceCode(_)));
assert_eq!(item.source_code.sources().len(), 1);
assert_eq!(item.abi().unwrap(), serde_json::from_str(DEPOSIT_CONTRACT_ABI).unwrap());
}

#[tokio::test]
#[serial]
async fn can_fetch_contract_source_code() {
Expand Down