Repository: gakonst/ethers-rs
Pull request #1608 (2022-08-18 12:45:24)

fix: support formatting large units
<!--
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 https://github.com/foundry-rs/foundry/issues/2830
<!--
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
* use `Decimal::Ten.pow()` instead of `10u64.pow`

<!--
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-core/Cargo.toml b/ethers-core/Cargo.toml
index 6358c3ae..0a51182c 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -41,7 +41,7 @@ cargo_metadata = { version = "0.15.0", optional = true }
 convert_case = { version = "0.5.0", optional = true }
 syn = { version = "1.0.99", optional = true }
 proc-macro2 = { version = "1.0.43", optional = true }
-rust_decimal = "1.26.1"
+rust_decimal = { version = "1.26.1", features = ["maths"] }
 
 [dev-dependencies]
 serde_json = { version = "1.0.64", default-features = false }
diff --git a/ethers-core/src/types/proof.rs b/ethers-core/src/types/proof.rs
index 011c3576..a1d9cc17 100644
--- a/ethers-core/src/types/proof.rs
+++ b/ethers-core/src/types/proof.rs
@@ -1,4 +1,4 @@
-use crate::types::{Address, Bytes, H256, U256};
+use crate::types::{Address, Bytes, H256, U256, U64};
 use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -11,13 +11,13 @@ pub struct StorageProof {
 #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct EIP1186ProofResponse {
-    address: Address,
-    balance: U256,
-    code_hash: H256,
-    nonce: U256,
-    storage_hash: H256,
-    account_proof: Vec<Bytes>,
-    storage_proof: Vec<StorageProof>,
+    pub address: Address,
+    pub balance: U256,
+    pub code_hash: H256,
+    pub nonce: U64,
+    pub storage_hash: H256,
+    pub account_proof: Vec<Bytes>,
+    pub storage_proof: Vec<StorageProof>,
 }
 
 #[cfg(test)]
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index b1cf9ca2..5e3b1c2d 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -155,9 +155,15 @@ where
     S: ToString,
     K: TryInto<Units, Error = ConversionError> + Copy,
 {
-    use rust_decimal::Decimal;
+    use rust_decimal::{Decimal, MathematicalOps};
+
     let num: Decimal = amount.to_string().parse()?;
-    let multiplier: Decimal = 10u64.pow(units.try_into()?.as_num()).into();
+    let exponent = units.try_into()?.as_num();
+
+    let multiplier = Decimal::TEN
+        .checked_powu(exponent.into())
+        .ok_or(rust_decimal::Error::ExceedsMaximumPossibleValue)?;
+
     let val =
         num.checked_mul(multiplier).ok_or(rust_decimal::Error::ExceedsMaximumPossibleValue)?;
     let u256_n: U256 = U256::from_dec_str(&val.round().to_string())?;
@@ -441,6 +447,14 @@ mod tests {
         assert_eq!(eth, "1.005633240123456789");
     }
 
+    #[test]
+    fn parse_large_units() {
+        let decimals = 27u32;
+        let val = "10.55";
+        let unit = parse_units(val, decimals).unwrap();
+        assert_eq!(unit.to_string(), "10550000000000000000000000000");
+    }
+
     #[test]
     fn test_parse_units() {
         let gwei = parse_units(1.5, 9).unwrap();
diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs
index 9e7ee807..3c1403ed 100644
--- a/ethers-providers/src/lib.rs
+++ b/ethers-providers/src/lib.rs
@@ -20,7 +20,7 @@ mod pending_escalator;
 pub use pending_escalator::EscalatingPending;
 
 mod log_query;
-pub use log_query::LogQuery;
+pub use log_query::{LogQuery, LogQueryError};
 
 mod stream;
 pub use futures_util::StreamExt;


Test patch:
diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs
index 5dd25efc..e8d2c441 100644
--- a/ethers-contract/tests/it/abigen.rs
+++ b/ethers-contract/tests/it/abigen.rs
@@ -557,17 +557,18 @@ async fn can_abiencoderv2_output() {
     assert_eq!(res, person);
 }
 
-#[test]
-fn can_gen_multi_etherscan() {
-    abigen!(
-        MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7";
-        MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2";
-    );
-
-    let provider = Arc::new(Provider::new(MockProvider::new()));
-    let _contract = MyContract::new(Address::default(), Arc::clone(&provider));
-    let _contract = MyContract2::new(Address::default(), provider);
-}
+// NOTE: this is commented out because this would result in compiler errors if key not set or
+// etherscan API not working #[test]
+// fn can_gen_multi_etherscan() {
+//     abigen!(
+//         MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7";
+//         MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2";
+//     );
+//
+//     let provider = Arc::new(Provider::new(MockProvider::new()));
+//     let _contract = MyContract::new(Address::default(), Arc::clone(&provider));
+//     let _contract = MyContract2::new(Address::default(), provider);
+// }
 
 #[test]
 fn can_gen_reserved_word_field_names() {