Repository: gakonst/ethers-rs (rust)

Issue:
Blocknumber is incorrectly decoded as hex value for etherscan client
**Description**

The block number returned by the etherscan client is decoded as hex value, but the client returns a decimal value.

```rust
use ethers_core::types::Chain;
use ethers_etherscan::{account::TokenQueryOption, Client};

#[tokio::main]
async fn main() {
    let client = Client::new_from_env(Chain::Mainnet).unwrap();
    let txs = client
        .get_erc20_token_transfer_events(
            TokenQueryOption::ByAddress(
                "0x4e83362442b8d1bec281594cea3050c8eb01311c"
                    .parse()
                    .unwrap(),
            ),
            None,
        )
        .await
        .unwrap();
    println!("{:?}", txs[0]);
}
```

Returns
```
ERC20TokenTransferEvent { block_number: Number(35816024), time_stamp: "1473433992", hash: 0x5f2cd76fd3656686e356bc02cc91d8d0726a16936fd08e67ed30467053225a86, nonce: 16, block_hash: 0x466dbced0a98e1b0490f787b51570850c8347960a4e92bd89acf85299e4cb9dc, from: 0x4e83362442b8d1bec281594cea3050c8eb01311c, contract_address: 0xecf8f87f810ecf450940c9f60066b4a7a501d6a7, to: Some(0xac75b73394c329376c214663d92156afa864a77f), value: 1213648186097498819919872, token_name: "", token_symbol: "", token_decimal: "1", transaction_index: 16, gas: 16777216, gas_price: Some(2199023255552), gas_used: 603735, cumulative_gas_used: 6833941, input: "deprecated", confirmations: 311965558 }
```

However, if you look at the tx: https://etherscan.io/tx/0x5f2cd76fd3656686e356bc02cc91d8d0726a16936fd08e67ed30467053225a86, the block number is: `2228258`, which is different from `block_number: Number(35816024)`. Note: `0x2228258 = 35816024`

See: https://github.com/gakonst/ethers-rs/blob/cb7e58664576c220ed656bfa191ac4065a052cce/ethers-core/src/types/block.rs#L579




Pull request description:
fix(etherscan): support stringified numbers in response
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
Closes #1507

the etherscan API returns numbers as strings 

```json
    {
      "blockNumber": "2228258",
      "timeStamp": "1473433992",
      "hash": "0x5f2cd76fd3656686e356bc02cc91d8d0726a16936fd08e67ed30467053225a86",
      "nonce": "10",
      "blockHash": "0x466dbced0a98e1b0490f787b51570850c8347960a4e92bd89acf85299e4cb9dc",
      "from": "0x4e83362442b8d1bec281594cea3050c8eb01311c",
      "contractAddress": "0xecf8f87f810ecf450940c9f60066b4a7a501d6a7",
      "to": "0xac75b73394c329376c214663d92156afa864a77f",
      "value": "101000000000000000000",
      "tokenName": "",
      "tokenSymbol": "",
      "tokenDecimal": "1",
      "transactionIndex": "10",
      "gas": "1000000",
      "gasPrice": "20000000000",
      "gasUsed": "93657",
      "cumulativeGasUsed": "684715",
      "input": "deprecated",
      "confirmations": "13002487"
    }
```

wich results in wrong values
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [x] Added Tests
- [ ] Added Documentation
- [ ] Updated the changelog


Patch:
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs
index d7fb7b5e..9c098635 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs
@@ -77,7 +77,7 @@ impl Context {
             }
 
              impl #ethers_contract::EthLogDecode for #enum_name {
-                fn decode_log(log: &#ethers_core::abi::RawLog) -> Result<Self, #ethers_core::abi::Error>
+                fn decode_log(log: &#ethers_core::abi::RawLog) -> ::std::result::Result<Self, #ethers_core::abi::Error>
                 where
                     Self: Sized,
                 {
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
index 912444c7..3894802b 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
@@ -91,7 +91,7 @@ impl Context {
             ///    let msg = greeter_contract.greet().call().await.unwrap();
             /// # }
             /// ```
-            pub fn deploy<T: #ethers_core::abi::Tokenize >(client: ::std::sync::Arc<M>, constructor_args: T) -> Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
+            pub fn deploy<T: #ethers_core::abi::Tokenize >(client: ::std::sync::Arc<M>, constructor_args: T) -> ::std::result::Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
                let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client);
                let deployer = factory.deploy(constructor_args)?;
                let deployer = #ethers_contract::ContractDeployer::new(deployer);
@@ -219,7 +219,7 @@ impl Context {
             }
 
         impl  #ethers_core::abi::AbiDecode for #enum_name {
-            fn decode(data: impl AsRef<[u8]>) -> Result<Self, #ethers_core::abi::AbiError> {
+            fn decode(data: impl AsRef<[u8]>) -> ::std::result::Result<Self, #ethers_core::abi::AbiError> {
                  #(
                     if let Ok(decoded) = <#struct_names as #ethers_core::abi::AbiDecode>::decode(data.as_ref()) {
                         return Ok(#enum_name::#variant_names(decoded))
diff --git a/ethers-contract/ethers-contract-derive/src/abi_ty.rs b/ethers-contract/ethers-contract-derive/src/abi_ty.rs
index 18b43669..442d9d1d 100644
--- a/ethers-contract/ethers-contract-derive/src/abi_ty.rs
+++ b/ethers-contract/ethers-contract-derive/src/abi_ty.rs
@@ -152,7 +152,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
              #tokenize_predicates
          {
 
-             fn from_token(token: #core_crate::abi::Token) -> Result<Self, #core_crate::abi::InvalidOutputType> where
+             fn from_token(token: #core_crate::abi::Token) -> ::std::result::Result<Self, #core_crate::abi::InvalidOutputType> where
                  Self: Sized {
                 #from_token_impl
              }
@@ -174,7 +174,7 @@ fn tokenize_unit_type(name: &Ident) -> TokenStream {
     let ethers_core = ethers_core_crate();
     quote! {
          impl #ethers_core::abi::Tokenizable for #name {
-             fn from_token(token: #ethers_core::abi::Token) -> Result<Self, #ethers_core::abi::InvalidOutputType> where
+             fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType> where
                  Self: Sized {
                 if let #ethers_core::abi::Token::Tuple(tokens) = token {
                     if !tokens.is_empty() {
@@ -210,7 +210,7 @@ fn tokenize_unit_type(name: &Ident) -> TokenStream {
 fn tokenize_enum<'a>(
     enum_name: &Ident,
     variants: impl Iterator<Item = &'a Variant> + 'a,
-) -> Result<TokenStream, Error> {
+) -> ::std::result::Result<TokenStream, Error> {
     let ethers_core = ethers_core_crate();
 
     let mut into_tokens = TokenStream::new();
@@ -252,7 +252,7 @@ fn tokenize_enum<'a>(
     Ok(quote! {
          impl #ethers_core::abi::Tokenizable for #enum_name {
 
-             fn from_token(token: #ethers_core::abi::Token) -> Result<Self, #ethers_core::abi::InvalidOutputType> where
+             fn from_token(token: #ethers_core::abi::Token) -> ::std::result::Result<Self, #ethers_core::abi::InvalidOutputType> where
                  Self: Sized {
                 #from_tokens
                 Err(#ethers_core::abi::InvalidOutputType("Failed to decode all type variants".to_string()))
diff --git a/ethers-contract/ethers-contract-derive/src/abigen.rs b/ethers-contract/ethers-contract-derive/src/abigen.rs
index f1c47e3b..a91334ed 100644
--- a/ethers-contract/ethers-contract-derive/src/abigen.rs
+++ b/ethers-contract/ethers-contract-derive/src/abigen.rs
@@ -27,7 +27,7 @@ pub(crate) struct Contracts {
 }
 
 impl Contracts {
-    pub(crate) fn expand(self) -> Result<TokenStream2, syn::Error> {
+    pub(crate) fn expand(self) -> ::std::result::Result<TokenStream2, syn::Error> {
         let mut expansions = Vec::with_capacity(self.inner.len());
 
         // expand all contracts
diff --git a/ethers-contract/ethers-contract-derive/src/call.rs b/ethers-contract/ethers-contract-derive/src/call.rs
index 15f901a0..f86163ed 100644
--- a/ethers-contract/ethers-contract-derive/src/call.rs
+++ b/ethers-contract/ethers-contract-derive/src/call.rs
@@ -95,7 +95,7 @@ pub fn derive_trait_impls(
         }
 
         impl  #core_crate::abi::AbiDecode for #struct_name {
-            fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, #core_crate::abi::AbiError> {
+            fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #core_crate::abi::AbiError> {
                 #decode_impl
             }
         }
diff --git a/ethers-contract/ethers-contract-derive/src/codec.rs b/ethers-contract/ethers-contract-derive/src/codec.rs
index 54a577f2..15472fc2 100644
--- a/ethers-contract/ethers-contract-derive/src/codec.rs
+++ b/ethers-contract/ethers-contract-derive/src/codec.rs
@@ -12,7 +12,7 @@ pub fn derive_codec_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
 
     quote! {
         impl  #core_crate::abi::AbiDecode for #name {
-            fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, #core_crate::abi::AbiError> {
+            fn decode(bytes: impl AsRef<[u8]>) -> ::std::result::Result<Self, #core_crate::abi::AbiError> {
                 if let #core_crate::abi::ParamType::Tuple(params) = <Self as #core_crate::abi::AbiType>::param_type() {
                   let tokens = #core_crate::abi::decode(&params, bytes.as_ref())?;
                   Ok(<Self as #core_crate::abi::Tokenizable>::from_token(#core_crate::abi::Token::Tuple(tokens))?)
diff --git a/ethers-contract/ethers-contract-derive/src/event.rs b/ethers-contract/ethers-contract-derive/src/event.rs
index 05d02f4f..0e56314f 100644
--- a/ethers-contract/ethers-contract-derive/src/event.rs
+++ b/ethers-contract/ethers-contract-derive/src/event.rs
@@ -95,7 +95,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
                 #abi.into()
             }
 
-            fn decode_log(log: &#core_crate::abi::RawLog) -> Result<Self, #core_crate::abi::Error> where Self: Sized {
+            fn decode_log(log: &#core_crate::abi::RawLog) -> ::std::result::Result<Self, #core_crate::abi::Error> where Self: Sized {
                 #decode_log_impl
             }
 
diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs
index ce0d4e28..03a2dc33 100644
--- a/ethers-core/src/types/block.rs
+++ b/ethers-core/src/types/block.rs
@@ -764,11 +764,13 @@ mod tests {
     #[test]
     fn test_next_block_base_fee() {
         // <https://etherscan.io/block/14402566>
-        let mut block_14402566 = Block::<TxHash>::default();
-        block_14402566.number = Some(U64::from(14402566u64));
-        block_14402566.base_fee_per_gas = Some(U256::from(36_803_013_756u128));
-        block_14402566.gas_limit = U256::from(30_087_887u128);
-        block_14402566.gas_used = U256::from(2_023_848u128);
+        let block_14402566: Block<TxHash> = Block {
+            number: Some(U64::from(14402566u64)),
+            base_fee_per_gas: Some(U256::from(36_803_013_756u128)),
+            gas_limit: U256::from(30_087_887u128),
+            gas_used: U256::from(2_023_848u128),
+            ..Default::default()
+        };
 
         assert_eq!(block_14402566.base_fee_per_gas, Some(U256::from(36_803_013_756u128)));
         assert_eq!(block_14402566.gas_target(), U256::from(15_043_943u128));
@@ -776,11 +778,13 @@ mod tests {
         assert_eq!(block_14402566.next_block_base_fee(), Some(U256::from(32_821_521_542u128)));
 
         // https://etherscan.io/block/14402712
-        let mut block_14402712 = Block::<TxHash>::default();
-        block_14402712.number = Some(U64::from(14402712u64));
-        block_14402712.base_fee_per_gas = Some(U256::from(24_870_031_149u128));
-        block_14402712.gas_limit = U256::from(30_000_000u128);
-        block_14402712.gas_used = U256::from(29_999_374u128);
+        let block_14402712: Block<TxHash> = Block {
+            number: Some(U64::from(14402712u64)),
+            base_fee_per_gas: Some(U256::from(24_870_031_149u128)),
+            gas_limit: U256::from(30_000_000u128),
+            gas_used: U256::from(29_999_374u128),
+            ..Default::default()
+        };
 
         assert_eq!(block_14402712.base_fee_per_gas, Some(U256::from(24_870_031_149u128)));
         assert_eq!(block_14402712.gas_target(), U256::from(15_000_000u128));
diff --git a/ethers-core/src/types/serde_helpers.rs b/ethers-core/src/types/serde_helpers.rs
index bd83a573..5672279a 100644
--- a/ethers-core/src/types/serde_helpers.rs
+++ b/ethers-core/src/types/serde_helpers.rs
@@ -1,9 +1,11 @@
 //! Some convenient serde helpers
 
 use crate::types::{BlockNumber, U256};
-use ethabi::ethereum_types::FromDecStrErr;
 use serde::{Deserialize, Deserializer};
-use std::convert::TryFrom;
+use std::{
+    convert::{TryFrom, TryInto},
+    str::FromStr,
+};
 
 /// Helper type to parse both `u64` and `U256`
 #[derive(Copy, Clone, Deserialize)]
@@ -22,7 +24,21 @@ impl From<Numeric> for U256 {
     }
 }
 
-/// Helper type to parse both `u64` and `U256`
+impl FromStr for Numeric {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if let Ok(val) = s.parse::<u128>() {
+            Ok(Numeric::U256(val.into()))
+        } else if s.starts_with("0x") {
+            U256::from_str(s).map(Numeric::U256).map_err(|err| err.to_string())
+        } else {
+            U256::from_dec_str(s).map(Numeric::U256).map_err(|err| err.to_string())
+        }
+    }
+}
+
+/// Helper type to parse numeric strings, `u64` and `U256`
 #[derive(Deserialize, Debug, Clone)]
 #[serde(untagged)]
 pub enum StringifiedNumeric {
@@ -32,7 +48,7 @@ pub enum StringifiedNumeric {
 }
 
 impl TryFrom<StringifiedNumeric> for U256 {
-    type Error = FromDecStrErr;
+    type Error = String;
 
     fn try_from(value: StringifiedNumeric) -> Result<Self, Self::Error> {
         match value {
@@ -41,14 +57,71 @@ impl TryFrom<StringifiedNumeric> for U256 {
             StringifiedNumeric::String(s) => {
                 if let Ok(val) = s.parse::<u128>() {
                     Ok(val.into())
+                } else if s.starts_with("0x") {
+                    U256::from_str(&s).map_err(|err| err.to_string())
                 } else {
-                    U256::from_dec_str(&s)
+                    U256::from_dec_str(&s).map_err(|err| err.to_string())
                 }
             }
         }
     }
 }
 
+/// Supports parsing numbers as strings
+///
+/// See <https://github.com/gakonst/ethers-rs/issues/1507>
+pub fn deserialize_stringified_numeric<'de, D>(deserializer: D) -> Result<U256, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let num = StringifiedNumeric::deserialize(deserializer)?;
+    num.try_into().map_err(serde::de::Error::custom)
+}
+
+/// Supports parsing numbers as strings
+///
+/// See <https://github.com/gakonst/ethers-rs/issues/1507>
+pub fn deserialize_stringified_numeric_opt<'de, D>(
+    deserializer: D,
+) -> Result<Option<U256>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    if let Some(num) = Option::<StringifiedNumeric>::deserialize(deserializer)? {
+        num.try_into().map(Some).map_err(serde::de::Error::custom)
+    } else {
+        Ok(None)
+    }
+}
+
+/// Supports parsing u64
+///
+/// See <https://github.com/gakonst/ethers-rs/issues/1507>
+pub fn deserialize_stringified_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let num = StringifiedNumeric::deserialize(deserializer)?;
+    let num: U256 = num.try_into().map_err(serde::de::Error::custom)?;
+    num.try_into().map_err(serde::de::Error::custom)
+}
+
+/// Supports parsing u64
+///
+/// See <https://github.com/gakonst/ethers-rs/issues/1507>
+pub fn deserialize_stringified_u64_opt<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    if let Some(num) = Option::<StringifiedNumeric>::deserialize(deserializer)? {
+        let num: U256 = num.try_into().map_err(serde::de::Error::custom)?;
+        let num: u64 = num.try_into().map_err(serde::de::Error::custom)?;
+        Ok(Some(num))
+    } else {
+        Ok(None)
+    }
+}
+
 /// Helper type to deserialize sequence of numbers
 #[derive(Deserialize)]
 #[serde(untagged)]
@@ -94,6 +167,42 @@ where
     Ok(num)
 }
 
+/// Helper type to parse numeric strings, `u64` and `U256`
+#[derive(Deserialize, Debug, Clone)]
+#[serde(untagged)]
+pub enum StringifiedBlockNumber {
+    Numeric(StringifiedNumeric),
+    BlockNumber(BlockNumber),
+}
+
+impl TryFrom<StringifiedBlockNumber> for BlockNumber {
+    type Error = String;
+
+    fn try_from(value: StringifiedBlockNumber) -> Result<Self, Self::Error> {
+        match value {
+            StringifiedBlockNumber::Numeric(num) => {
+                let num = U256::try_from(num)
+                    .and_then(|num| u64::try_from(num).map_err(str::to_string))?;
+                Ok(BlockNumber::Number(num.into()))
+            }
+            StringifiedBlockNumber::BlockNumber(b) => Ok(b),
+        }
+    }
+}
+
+/// Supports parsing block number as strings
+///
+/// See <https://github.com/gakonst/ethers-rs/issues/1507>
+pub fn deserialize_stringified_block_number<'de, D>(
+    deserializer: D,
+) -> Result<BlockNumber, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let num = StringifiedBlockNumber::deserialize(deserializer)?;
+    num.try_into().map_err(serde::de::Error::custom)
+}
+
 /// Various block number representations, See [`lenient_block_number()`]
 #[derive(Clone, Copy, Deserialize)]
 #[serde(untagged)]
diff --git a/ethers-core/src/types/transaction/request.rs b/ethers-core/src/types/transaction/request.rs
index 25270d92..34a47f86 100644
--- a/ethers-core/src/types/transaction/request.rs
+++ b/ethers-core/src/types/transaction/request.rs
@@ -547,7 +547,7 @@ mod tests {
 
         let addresses = raw_addresses
             .iter()
-            .map(|addr| NameOrAddress::Address(Address::from_str(*addr).unwrap()));
+            .map(|addr| NameOrAddress::Address(Address::from_str(addr).unwrap()));
 
         // decoding will do sender recovery and we don't expect any of these to error, so we should
         // check that the address matches for each decoded transaction
diff --git a/ethers-etherscan/src/account.rs b/ethers-etherscan/src/account.rs
index 7f01c578..a33f4484 100644
--- a/ethers-etherscan/src/account.rs
+++ b/ethers-etherscan/src/account.rs
@@ -1,17 +1,15 @@
+use crate::{Client, EtherscanError, Query, Response, Result};
+use ethers_core::{
+    abi::Address,
+    types::{serde_helpers::*, BlockNumber, Bytes, H256, U256},
+};
+use serde::{Deserialize, Serialize};
 use std::{
     borrow::Cow,
     collections::HashMap,
     fmt::{Display, Error, Formatter},
 };
 
-use ethers_core::{
-    abi::Address,
-    types::{BlockNumber, Bytes, H256, U256, U64},
-};
-use serde::{Deserialize, Serialize};
-
-use crate::{Client, EtherscanError, Query, Response, Result};
-
 /// The raw response from the balance-related API endpoints
 #[derive(Debug, Serialize, Deserialize)]
 pub struct AccountBalance {
@@ -100,6 +98,7 @@ impl<T> GenesisOption<T> {
 #[serde(rename_all = "camelCase")]
 pub struct NormalTransaction {
     pub is_error: String,
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     #[serde(with = "jsonstring")]
@@ -108,12 +107,16 @@ pub struct NormalTransaction {
     pub nonce: GenesisOption<U256>,
     #[serde(with = "jsonstring")]
     pub block_hash: GenesisOption<U256>,
-    pub transaction_index: Option<U64>,
+    #[serde(deserialize_with = "deserialize_stringified_u64_opt")]
+    pub transaction_index: Option<u64>,
     #[serde(with = "jsonstring")]
     pub from: GenesisOption<Address>,
     pub to: Option<Address>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub value: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric_opt")]
     pub gas_price: Option<U256>,
     #[serde(rename = "txreceipt_status")]
     pub tx_receipt_status: String,
@@ -121,21 +124,26 @@ pub struct NormalTransaction {
     pub input: GenesisOption<Bytes>,
     #[serde(with = "jsonstring")]
     pub contract_address: GenesisOption<Address>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas_used: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub cumulative_gas_used: U256,
-    pub confirmations: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub confirmations: u64,
 }
 
 /// The raw response from the internal transaction list API endpoint
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct InternalTransaction {
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     pub hash: H256,
     pub from: Address,
     #[serde(with = "jsonstring")]
     pub to: GenesisOption<Address>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub value: U256,
     #[serde(with = "jsonstring")]
     pub contract_address: GenesisOption<Address>,
@@ -143,7 +151,9 @@ pub struct InternalTransaction {
     pub input: GenesisOption<Bytes>,
     #[serde(rename = "type")]
     pub result_type: String,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas_used: U256,
     pub trace_id: String,
     pub is_error: String,
@@ -154,35 +164,46 @@ pub struct InternalTransaction {
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct ERC20TokenTransferEvent {
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     pub hash: H256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub nonce: U256,
     pub block_hash: H256,
     pub from: Address,
     pub contract_address: Address,
     pub to: Option<Address>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub value: U256,
     pub token_name: String,
     pub token_symbol: String,
     pub token_decimal: String,
-    pub transaction_index: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub transaction_index: u64,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric_opt")]
     pub gas_price: Option<U256>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas_used: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub cumulative_gas_used: U256,
     /// deprecated
     pub input: String,
-    pub confirmations: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub confirmations: u64,
 }
 
 /// The raw response from the ERC721 transfer list API endpoint
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct ERC721TokenTransferEvent {
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     pub hash: H256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub nonce: U256,
     pub block_hash: H256,
     pub from: Address,
@@ -193,23 +214,31 @@ pub struct ERC721TokenTransferEvent {
     pub token_name: String,
     pub token_symbol: String,
     pub token_decimal: String,
-    pub transaction_index: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub transaction_index: u64,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric_opt")]
     pub gas_price: Option<U256>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas_used: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub cumulative_gas_used: U256,
     /// deprecated
     pub input: String,
-    pub confirmations: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub confirmations: u64,
 }
 
 /// The raw response from the ERC1155 transfer list API endpoint
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct ERC1155TokenTransferEvent {
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     pub hash: H256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub nonce: U256,
     pub block_hash: H256,
     pub from: Address,
@@ -220,20 +249,27 @@ pub struct ERC1155TokenTransferEvent {
     pub token_value: String,
     pub token_name: String,
     pub token_symbol: String,
-    pub transaction_index: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub transaction_index: u64,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric_opt")]
     pub gas_price: Option<U256>,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub gas_used: U256,
+    #[serde(deserialize_with = "deserialize_stringified_numeric")]
     pub cumulative_gas_used: U256,
     /// deprecated
     pub input: String,
-    pub confirmations: U64,
+    #[serde(deserialize_with = "deserialize_stringified_u64")]
+    pub confirmations: u64,
 }
 
 /// The raw response from the mined blocks API endpoint
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct MinedBlock {
+    #[serde(deserialize_with = "deserialize_stringified_block_number")]
     pub block_number: BlockNumber,
     pub time_stamp: String,
     pub block_reward: String,
@@ -739,8 +775,12 @@ mod tests {
                     ),
                     None,
                 )
-                .await;
-            assert!(txs.is_ok());
+                .await
+                .unwrap();
+            let tx = txs.get(0).unwrap();
+            assert_eq!(tx.gas_used, 93657u64.into());
+            assert_eq!(tx.nonce, 10u64.into());
+            assert_eq!(tx.block_number, 2228258u64.into());
         })
         .await
     }
diff --git a/ethers-etherscan/src/gas.rs b/ethers-etherscan/src/gas.rs
index e9c7e1de..94e93b1e 100644
--- a/ethers-etherscan/src/gas.rs
+++ b/ethers-etherscan/src/gas.rs
@@ -70,15 +70,11 @@ impl Client {
 
 #[cfg(test)]
 mod tests {
-    use std::time::Duration;
-
-    use serial_test::serial;
-
-    use ethers_core::types::Chain;
-
-    use crate::tests::run_at_least_duration;
-
     use super::*;
+    use crate::tests::run_at_least_duration;
+    use ethers_core::types::Chain;
+    use serial_test::serial;
+    use std::time::Duration;
 
     #[tokio::test]
     #[serial]
diff --git a/ethers-etherscan/src/transaction.rs b/ethers-etherscan/src/transaction.rs
index e916d55a..9259b097 100644
--- a/ethers-etherscan/src/transaction.rs
+++ b/ethers-etherscan/src/transaction.rs
@@ -52,13 +52,10 @@ impl Client {
 
 #[cfg(test)]
 mod tests {
-    use std::time::Duration;
-
-    use serial_test::serial;
-
-    use crate::{tests::run_at_least_duration, Chain};
-
     use super::*;
+    use crate::{tests::run_at_least_duration, Chain};
+    use serial_test::serial;
+    use std::time::Duration;
 
     #[tokio::test]
     #[serial]


Test patch:
diff --git a/ethers-contract/tests/it/common/derive.rs b/ethers-contract/tests/it/common/derive.rs
index d856cbb5..3198cc9e 100644
--- a/ethers-contract/tests/it/common/derive.rs
+++ b/ethers-contract/tests/it/common/derive.rs
@@ -605,3 +605,16 @@ fn eth_display_works_on_ethers_bytes() {
     let s = format!("{}", call);
     assert_eq!(s, "0xaaaaaa");
 }
+
+#[test]
+fn can_use_result_name() {
+    abigen!(
+        ResultContract,
+        r#"[
+           struct Result {uint256 result;}
+           result(Result result) (uint256)
+        ]"#,
+    );
+
+    let _call = ResultCall { result: Result { result: U256::zero() } };
+}