Repository: gakonst/ethers-rs
Pull request #2325 (2023-04-05 12:37:26)

fix(etherscan)!: change the `GasOracle` fields to be `f64`
<!--
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

<!--
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.
-->
Closes  #2324

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->
This updates the `GasOracle`'s `{safe_gase_price,propose_gas_price,fast_gas_price}` from `u64` to `f64`

BREAKING CHANGE: This considered a public api breaking change, since `f64` and `u64` is not compatible with each other.

## PR Checklist

-   [x] Added Tests
-   [x] Added Documentation
-   [x] Breaking changes


shekohex: Tested Locally with the repo in #2324 with my new updates and works correctly :tada: !

```rs
INFO ethers_etherscan_issue: Gas Oracle Info, gas_oracle: Ok(GasOracle { safe_gas_price: 110.4, propose_gas_price: 127.4, fast_gas_price: 134.0, last_block: 41173362, suggested_base_fee: 80.380853619, gas_used_ratio: [0.447337990736135, 0.250362882144311, 0.414055028555671, 0.799576101506245, 0.419134294964152] })
    at src/main.rs:39
```

Patch:
diff --git a/ethers-etherscan/src/gas.rs b/ethers-etherscan/src/gas.rs
index e58fc2f3..e8eacdd4 100644
--- a/ethers-etherscan/src/gas.rs
+++ b/ethers-etherscan/src/gas.rs
@@ -7,11 +7,11 @@ use std::{collections::HashMap, str::FromStr};
 #[serde(rename_all = "PascalCase")]
 pub struct GasOracle {
     #[serde(deserialize_with = "deserialize_number_from_string")]
-    pub safe_gas_price: u64,
+    pub safe_gas_price: f64,
     #[serde(deserialize_with = "deserialize_number_from_string")]
-    pub propose_gas_price: u64,
+    pub propose_gas_price: f64,
     #[serde(deserialize_with = "deserialize_number_from_string")]
-    pub fast_gas_price: u64,
+    pub fast_gas_price: f64,
     #[serde(deserialize_with = "deserialize_number_from_string")]
     pub last_block: u64,
     #[serde(deserialize_with = "deserialize_number_from_string")]
@@ -82,3 +82,28 @@ impl Client {
         Ok(response.result)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn response_works() {
+        // Response from Polygon mainnet at 2023-04-05
+        let v = r#"{
+            "status": "1",
+            "message": "OK",
+            "result": {
+                "LastBlock": "41171167",
+                "SafeGasPrice": "119.9",
+                "ProposeGasPrice": "141.9",
+                "FastGasPrice": "142.9",
+                "suggestBaseFee": "89.82627877",
+                "gasUsedRatio": "0.399191166666667,0.4847166,0.997667533333333,0.538075133333333,0.343416033333333",
+                "UsdPrice": "1.15"
+            }
+        }"#;
+        let gas_oracle: Response<GasOracle> = serde_json::from_str(v).unwrap();
+        assert_eq!(gas_oracle.message, "OK");
+    }
+}
diff --git a/ethers-middleware/src/gas_oracle/etherscan.rs b/ethers-middleware/src/gas_oracle/etherscan.rs
index 9d8da4c0..481f18e7 100644
--- a/ethers-middleware/src/gas_oracle/etherscan.rs
+++ b/ethers-middleware/src/gas_oracle/etherscan.rs
@@ -1,6 +1,6 @@
 use super::{GasCategory, GasOracle, GasOracleError, Result, GWEI_TO_WEI_U256};
 use async_trait::async_trait;
-use ethers_core::types::U256;
+use ethers_core::{types::U256, utils::parse_units};
 use ethers_etherscan::Client;
 use std::ops::{Deref, DerefMut};
 
@@ -44,6 +44,8 @@ impl GasOracle for Etherscan {
             GasCategory::Fast => result.fast_gas_price,
             _ => unreachable!(),
         };
+        // returned gas prices are f64 value in gwei
+        let gas_price = parse_units(gas_price, "gwei")?;
         Ok(U256::from(gas_price) * GWEI_TO_WEI_U256)
     }
 
diff --git a/ethers-middleware/src/gas_oracle/mod.rs b/ethers-middleware/src/gas_oracle/mod.rs
index 40c1bf42..0c32de41 100644
--- a/ethers-middleware/src/gas_oracle/mod.rs
+++ b/ethers-middleware/src/gas_oracle/mod.rs
@@ -89,6 +89,8 @@ pub enum GasOracleError {
     /// Error thrown when the provider failed.
     #[error("Provider error: {0}")]
     ProviderError(#[from] Box<dyn Error + Send + Sync>),
+    #[error("Failed to parse gas values: {0}")]
+    ConversionError(#[from] ethers_core::utils::ConversionError),
 }
 
 /// An Ethereum gas price oracle.


Test patch:
diff --git a/ethers-etherscan/tests/it/gas.rs b/ethers-etherscan/tests/it/gas.rs
index a8754ee5..7c8e548e 100644
--- a/ethers-etherscan/tests/it/gas.rs
+++ b/ethers-etherscan/tests/it/gas.rs
@@ -34,9 +34,9 @@ async fn gas_oracle_success() {
 
         let oracle = result.unwrap();
 
-        assert!(oracle.safe_gas_price > 0);
-        assert!(oracle.propose_gas_price > 0);
-        assert!(oracle.fast_gas_price > 0);
+        assert!(oracle.safe_gas_price > 0f64);
+        assert!(oracle.propose_gas_price > 0f64);
+        assert!(oracle.fast_gas_price > 0f64);
         assert!(oracle.last_block > 0);
         assert!(oracle.suggested_base_fee > 0.0);
         assert!(!oracle.gas_used_ratio.is_empty());