From b05136e173f83f3c59280501a3fd4b04f7356e6f Mon Sep 17 00:00:00 2001 From: th4s Date: Thu, 10 Mar 2022 22:36:42 +0100 Subject: [PATCH 01/18] Fix websocket connection request (#1005) In tungstenite 0.17 a change was introduced considering the logic of `IntoClientRequest`. When using this trait on a `HttpRequest` it just uses the request without any changes to inititate the websocket connection. This makes it necessary for the consumer of the api to supply the correct http headers for initiating a websocket connection when using `IntoClientRequest` on `HttpRequest`. This commit adapts the logic in `connect_with_auth` to this new behavior. --- ethers-providers/src/transports/ws.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ethers-providers/src/transports/ws.rs b/ethers-providers/src/transports/ws.rs index ba27729b..31306e54 100644 --- a/ethers-providers/src/transports/ws.rs +++ b/ethers-providers/src/transports/ws.rs @@ -62,8 +62,7 @@ if_not_wasm! { use super::Authorization; use tracing::{debug, error, warn}; use http::Request as HttpRequest; - use http::Uri; - use std::str::FromStr; + use tungstenite::client::IntoClientRequest; } type Pending = oneshot::Sender>; @@ -137,9 +136,7 @@ impl Ws { /// Initializes a new WebSocket Client #[cfg(not(target_arch = "wasm32"))] - pub async fn connect( - url: impl tungstenite::client::IntoClientRequest + Unpin, - ) -> Result { + pub async fn connect(url: impl IntoClientRequest + Unpin) -> Result { let (ws, _) = connect_async(url).await?; Ok(Self::new(ws)) } @@ -147,11 +144,10 @@ impl Ws { /// Initializes a new WebSocket Client with authentication #[cfg(not(target_arch = "wasm32"))] pub async fn connect_with_auth( - uri: impl AsRef + Unpin, + uri: impl IntoClientRequest + Unpin, auth: Authorization, ) -> Result { - let mut request: HttpRequest<()> = - HttpRequest::builder().method("GET").uri(Uri::from_str(uri.as_ref())?).body(())?; + let mut request: HttpRequest<()> = uri.into_client_request()?; let mut auth_value = http::HeaderValue::from_str(&auth.to_string())?; auth_value.set_sensitive(true); From bbb1ce31a7205147c25580f0cd10ac0e13456691 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Fri, 11 Mar 2022 08:16:15 -0800 Subject: [PATCH 02/18] chore: bump svm-rs --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8d09a912..d7ad5f8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,7 +3575,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" version = "0.2.9" -source = "git+https://github.com/roynalnaruto/svm-rs#8e33f55fa2a2afb937749e31b2ffa42600bfe216" +source = "git+https://github.com/roynalnaruto/svm-rs#76a9779c1985ca39192f8254494d72f5802602df" dependencies = [ "anyhow", "cfg-if 1.0.0", From e5dbeb6b28e1cc02486df42c1d20b0d4bcf2e0e5 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 11 Mar 2022 17:43:48 +0100 Subject: [PATCH 03/18] revert: use simple change filter again (#1008) * chore: rollback dirty detection * docs: caching docs --- ethers-solc/src/cache.rs | 69 ++++++++---------------------- ethers-solc/src/compile/project.rs | 33 ++++++++++++++ 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs index faf07465..9c0edb8e 100644 --- a/ethers-solc/src/cache.rs +++ b/ethers-solc/src/cache.rs @@ -29,7 +29,7 @@ const ETHERS_FORMAT_VERSION: &str = "ethers-rs-sol-cache-2"; /// The file name of the default cache file pub const SOLIDITY_FILES_CACHE_FILENAME: &str = "solidity-files-cache.json"; -/// A hardhat compatible cache representation +/// A multi version cache file #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct SolFilesCache { #[serde(rename = "_format")] @@ -593,62 +593,35 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { } } - /// Returns only dirty sources that: + /// Returns only those sources that /// - are new /// - were changed /// - their imports were changed /// - their artifact is missing - /// This also includes their respective imports fn filter(&mut self, sources: Sources, version: &Version) -> Sources { self.fill_hashes(&sources); - - let mut imports_of_dirty = HashSet::new(); - // separates all source files that fit the criteria (dirty) from those that don't (clean) - let (mut dirty_sources, clean_sources) = sources + sources .into_iter() - .map(|(file, source)| self.filter_source(file, source, version)) - .fold( - (Sources::default(), Vec::new()), - |(mut dirty_sources, mut clean_sources), source| { - if source.dirty { - // mark all files that are imported by a dirty file - imports_of_dirty.extend(self.edges.all_imported_nodes(source.idx)); - dirty_sources.insert(source.file, source.source); - } else { - clean_sources.push(source); - } - - (dirty_sources, clean_sources) - }, - ); - - for clean_source in clean_sources { - let FilteredSource { file, source, idx, .. } = clean_source; - if imports_of_dirty.contains(&idx) { - // file is imported by a dirty file - dirty_sources.insert(file, source); - } else { - self.insert_filtered_source(file, source, version.clone()); - } - } - - // track dirty sources internally - for (file, source) in dirty_sources.iter() { - self.insert_new_cache_entry(file, source, version.clone()); - } - - dirty_sources + .filter_map(|(file, source)| self.requires_solc(file, source, version)) + .collect() } - /// Returns the state of the given source file. - fn filter_source(&self, file: PathBuf, source: Source, version: &Version) -> FilteredSource { - let idx = self.edges.node_id(&file); + /// Returns `Some` if the file _needs_ to be compiled and `None` if the artifact can be reu-used + fn requires_solc( + &mut self, + file: PathBuf, + source: Source, + version: &Version, + ) -> Option<(PathBuf, Source)> { if !self.is_dirty(&file, version) && self.edges.imports(&file).iter().all(|file| !self.is_dirty(file, version)) { - FilteredSource { file, source, idx, dirty: false } + self.insert_filtered_source(file, source, version.clone()); + None } else { - FilteredSource { file, source, idx, dirty: true } + self.insert_new_cache_entry(&file, &source, version.clone()); + + Some((file, source)) } } @@ -701,14 +674,6 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { } } -/// Helper type to represent the state of a source file -struct FilteredSource { - file: PathBuf, - source: Source, - idx: usize, - dirty: bool, -} - /// Abstraction over configured caching which can be either non-existent or an already loaded cache #[allow(clippy::large_enum_variant)] #[derive(Debug)] diff --git a/ethers-solc/src/compile/project.rs b/ethers-solc/src/compile/project.rs index 19e36a3c..31174ccb 100644 --- a/ethers-solc/src/compile/project.rs +++ b/ethers-solc/src/compile/project.rs @@ -73,6 +73,33 @@ //! file in the VFS under `dapp-bin/library/math.sol`. If the file is not available there, the //! source unit name will be passed to the Host Filesystem Loader, which will then look in //! `/project/dapp-bin/library/iterable_mapping.sol` +//! +//! +//! ### Caching and Change detection +//! +//! If caching is enabled in the [Project](crate::Project) a cache file will be created upon a +//! successful solc build. The [cache file](crate::SolFilesCache) stores metadata for all the files +//! that were provided to solc. +//! For every file the cache file contains a dedicated [cache +//! entry](crate::CacheEntry), which represents the state of the file. A solidity file can contain +//! several contracts, for every contract a separate [artifact](crate::Artifact) is emitted. +//! Therefor the entry also tracks all artifacts emitted by a file. A solidity file can also be +//! compiled with several solc versions. +//! +//! For example in `A(<=0.8.10) imports C(>0.4.0)` and +//! `B(0.8.11) imports C(>0.4.0)`, both `A` and `B` import `C` but there's no solc version that's +//! compatible with `A` and `B`, in which case two sets are compiled: [`A`, `C`] and [`B`, `C`]. +//! This is reflected in the cache entry which tracks the file's artifacts by version. +//! +//! The cache makes it possible to detect changes during recompilation, so that only the changed, +//! dirty, files need to be passed to solc. A file will be considered as dirty if: +//! - the file is new, not included in the existing cache +//! - the file was modified since the last compiler run, detected by comparing content hashes +//! - any of the imported files is dirty +//! - the file's artifacts don't exist, were deleted. +//! +//! Recompiling a project with cache enabled detects all files that meet these criteria and provides +//! solc with only these dirty files instead of the entire source set. use crate::{ artifact_output::Artifacts, @@ -283,7 +310,13 @@ impl CompilerSources { sources .into_iter() .map(|(solc, (version, sources))| { + tracing::trace!("Filtering {} sources for {}", sources.len(), version); let sources = cache.filter(sources, &version); + tracing::trace!( + "Detected {} dirty sources {:?}", + sources.len(), + sources.keys() + ); (solc, (version, sources)) }) .collect() From 341e98b4b0913c14add343a797b248eee3ecc370 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Fri, 11 Mar 2022 13:42:46 -0800 Subject: [PATCH 04/18] fix: use svm-rs with fixed solc checksums --- Cargo.lock | 2 +- ethers-solc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7ad5f8d..5c6ecdfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,7 +3575,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" version = "0.2.9" -source = "git+https://github.com/roynalnaruto/svm-rs#76a9779c1985ca39192f8254494d72f5802602df" +source = "git+https://github.com/gakonst/svm-rs?branch=patch-1#4c46f8b37c4ccb011095a003b7a0a4aa0b3b1d60" dependencies = [ "anyhow", "cfg-if 1.0.0", diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index 31e3d64b..05f925d0 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -42,7 +42,7 @@ rayon = "1.5.1" home = "0.5.3" # SVM is not WASM compatible yet. # svm = { package = "svm-rs", default-features = false, version = "0.2.7", optional = true } -svm = { package = "svm-rs", default-features = false, git = "https://github.com/roynalnaruto/svm-rs", optional = true, features = ["blocking"] } +svm = { package = "svm-rs", default-features = false, git = "https://github.com/gakonst/svm-rs", branch = "patch-1", optional = true, features = ["blocking"] } [target.'cfg(target_arch = "wasm32")'.dependencies] # NOTE: this enables wasm compatibility for getrandom indirectly From 77c3b390160a1c2d187543aab3a9020db499ad81 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sat, 12 Mar 2022 05:16:20 -0800 Subject: [PATCH 05/18] chore: go back to upstream svm --- Cargo.lock | 2 +- ethers-solc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c6ecdfa..2173af74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,7 +3575,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" version = "0.2.9" -source = "git+https://github.com/gakonst/svm-rs?branch=patch-1#4c46f8b37c4ccb011095a003b7a0a4aa0b3b1d60" +source = "git+https://github.com/roynalnaruto/svm-rs#3aea7a7401bb5cf62e915d42ebdc764172452187" dependencies = [ "anyhow", "cfg-if 1.0.0", diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index 05f925d0..31e3d64b 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -42,7 +42,7 @@ rayon = "1.5.1" home = "0.5.3" # SVM is not WASM compatible yet. # svm = { package = "svm-rs", default-features = false, version = "0.2.7", optional = true } -svm = { package = "svm-rs", default-features = false, git = "https://github.com/gakonst/svm-rs", branch = "patch-1", optional = true, features = ["blocking"] } +svm = { package = "svm-rs", default-features = false, git = "https://github.com/roynalnaruto/svm-rs", optional = true, features = ["blocking"] } [target.'cfg(target_arch = "wasm32")'.dependencies] # NOTE: this enables wasm compatibility for getrandom indirectly From 367f3444ec627c6afa3bd9613644a6f903b3cb86 Mon Sep 17 00:00:00 2001 From: oblique Date: Sun, 13 Mar 2022 13:58:29 +0200 Subject: [PATCH 06/18] fix: Export `ethers_providers::IpcError` and `ethers_providers::QuorumError` (#1012) --- CHANGELOG.md | 12 +++++++----- ethers-providers/src/transports/mod.rs | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9002f81..b34a53d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,13 @@ ## ethers-providers +### Unreleased + +- Add support for basic and bearer authentication in http and non-wasm websockets. + [829](https://github.com/gakonst/ethers-rs/pull/829) +- Export `ethers_providers::IpcError` and `ethers_providers::QuorumError` + [1012](https://github.com/gakonst/ethers-rs/pull/1012) + ### 0.6.0 - re-export error types for `Http` and `Ws` providers in @@ -144,11 +151,6 @@ - Add support for `evm_snapshot` and `evm_revert` dev RPC methods. [640](https://github.com/gakonst/ethers-rs/pull/640) -### Unreleased - -- Add support for basic and bearer authentication in http and non-wasm websockets. - [829](https://github.com/gakonst/ethers-rs/pull/829) - ### 0.5.3 - Expose `ens` module [#435](https://github.com/gakonst/ethers-rs/pull/435) diff --git a/ethers-providers/src/transports/mod.rs b/ethers-providers/src/transports/mod.rs index b7a6c64e..58233ab4 100644 --- a/ethers-providers/src/transports/mod.rs +++ b/ethers-providers/src/transports/mod.rs @@ -22,7 +22,7 @@ macro_rules! if_not_wasm { #[cfg(all(target_family = "unix", feature = "ipc"))] mod ipc; #[cfg(all(target_family = "unix", feature = "ipc"))] -pub use ipc::Ipc; +pub use ipc::{Ipc, IpcError}; mod http; pub use self::http::{ClientError as HttpClientError, Provider as Http}; @@ -34,7 +34,7 @@ pub use ws::{ClientError as WsClientError, Ws}; mod quorum; pub(crate) use quorum::JsonRpcClientWrapper; -pub use quorum::{Quorum, QuorumProvider, WeightedProvider}; +pub use quorum::{Quorum, QuorumError, QuorumProvider, WeightedProvider}; mod mock; pub use mock::{MockError, MockProvider}; From ae125bcfc7cf0f49af7f05a4b44e603c2859818e Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sun, 13 Mar 2022 18:04:09 +0200 Subject: [PATCH 07/18] feat: rotating infura keys (#1017) * feat: add providers which rotate api keys * test: use rotating key in provider instead of hardcoded * chore: fmt --- Cargo.lock | 1 + ethers-middleware/tests/nonce_manager.rs | 9 ++--- ethers-middleware/tests/signer.rs | 13 ++---- ethers-providers/Cargo.toml | 1 + ethers-providers/src/lib.rs | 50 ++++++++++++++++++++++++ ethers-providers/src/provider.rs | 8 ++-- ethers-providers/tests/provider.rs | 17 ++------ 7 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2173af74..cf3431f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1322,6 +1322,7 @@ dependencies = [ "futures-util", "hex", "http", + "once_cell", "parking_lot", "pin-project", "reqwest", diff --git a/ethers-middleware/tests/nonce_manager.rs b/ethers-middleware/tests/nonce_manager.rs index 30b3fa03..5e0a23dc 100644 --- a/ethers-middleware/tests/nonce_manager.rs +++ b/ethers-middleware/tests/nonce_manager.rs @@ -4,14 +4,11 @@ async fn nonce_manager() { use ethers_core::types::*; use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware}; - use ethers_providers::{Http, Middleware, Provider}; + use ethers_providers::Middleware; use ethers_signers::{LocalWallet, Signer}; - use std::{convert::TryFrom, time::Duration}; + use std::time::Duration; - let provider = - Provider::::try_from("https://rinkeby.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778") - .unwrap() - .interval(Duration::from_millis(2000u64)); + let provider = ethers_providers::RINKEBY.provider().interval(Duration::from_millis(2000u64)); let chain_id = provider.get_chainid().await.unwrap().as_u64(); let wallet = std::env::var("RINKEBY_PRIVATE_KEY") diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs index e5cf3570..6ec3fea6 100644 --- a/ethers-middleware/tests/signer.rs +++ b/ethers-middleware/tests/signer.rs @@ -1,5 +1,5 @@ #![allow(unused)] -use ethers_providers::{Http, JsonRpcClient, Middleware, Provider}; +use ethers_providers::{Http, JsonRpcClient, Middleware, Provider, RINKEBY}; use ethers_core::{ types::{BlockNumber, TransactionRequest}, @@ -8,7 +8,7 @@ use ethers_core::{ use ethers_middleware::signer::SignerMiddleware; use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; use once_cell::sync::Lazy; -use std::{convert::TryFrom, sync::atomic::AtomicU8, time::Duration}; +use std::{convert::TryFrom, iter::Cycle, sync::atomic::AtomicU8, time::Duration}; static WALLETS: Lazy = Lazy::new(|| { TestWallets { @@ -54,10 +54,7 @@ async fn send_eth() { #[tokio::test] #[cfg(not(feature = "celo"))] async fn pending_txs_with_confirmations_testnet() { - let provider = - Provider::::try_from("https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27") - .unwrap() - .interval(Duration::from_millis(3000)); + let provider = RINKEBY.provider().interval(Duration::from_millis(3000)); let chain_id = provider.get_chainid().await.unwrap(); let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); let address = wallet.address(); @@ -97,9 +94,7 @@ async fn generic_pending_txs_test(provider: M, who: Address) { #[tokio::test] #[cfg(not(feature = "celo"))] async fn typed_txs() { - let provider = - Provider::::try_from("https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27") - .unwrap(); + let provider = RINKEBY.provider(); let chain_id = provider.get_chainid().await.unwrap(); let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml index 0d921325..56090340 100644 --- a/ethers-providers/Cargo.toml +++ b/ethers-providers/Cargo.toml @@ -39,6 +39,7 @@ tracing = { version = "0.1.32", default-features = false } tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] } bytes = { version = "1.1.0", default-features = false, optional = true } +once_cell = "1.10.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # tokio diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index d169bcc2..00e90d9c 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -657,3 +657,53 @@ pub trait CeloMiddleware: Middleware { self.provider().get_validators_bls_public_keys(block_id).await.map_err(FromErr::from) } } + +pub use test_provider::{GOERLI, MAINNET, RINKEBY, ROPSTEN}; + +/// Pre-instantiated Infura HTTP clients which rotate through multiple API keys +/// to prevent rate limits +pub mod test_provider { + use super::*; + use crate::Http; + use once_cell::sync::Lazy; + use std::{convert::TryFrom, iter::Cycle, slice::Iter, sync::Mutex}; + + // List of infura keys to rotate through so we don't get rate limited + const INFURA_KEYS: &[&str] = &[ + "6770454bc6ea42c58aac12978531b93f", + "7a8769b798b642f6933f2ed52042bd70", + "631fd9a6539644088297dc605d35fff3", + "16a8be88795540b9b3903d8de0f7baa5", + "f4a0bdad42674adab5fc0ac077ffab2b", + "5c812e02193c4ba793f8c214317582bd", + ]; + + pub static RINKEBY: Lazy = + Lazy::new(|| TestProvider::new(INFURA_KEYS, "rinkeby")); + pub static MAINNET: Lazy = + Lazy::new(|| TestProvider::new(INFURA_KEYS, "mainnet")); + pub static GOERLI: Lazy = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli")); + pub static ROPSTEN: Lazy = + Lazy::new(|| TestProvider::new(INFURA_KEYS, "ropsten")); + + #[derive(Debug)] + pub struct TestProvider { + network: String, + keys: Mutex>>, + } + + impl TestProvider { + pub fn new(keys: &'static [&'static str], network: &str) -> Self { + Self { keys: Mutex::new(keys.iter().cycle()), network: network.to_owned() } + } + + pub fn provider(&self) -> Provider { + let url = format!( + "https://{}.infura.io/v3/{}", + self.network, + self.keys.lock().unwrap().next().unwrap() + ); + Provider::try_from(url.as_str()).unwrap() + } + } +} diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index dee01796..6f6c7ea8 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -1488,12 +1488,10 @@ mod tests { }; use futures_util::StreamExt; - const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; - #[tokio::test] // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 async fn mainnet_resolve_name() { - let provider = Provider::::try_from(INFURA).unwrap(); + let provider = crate::test_provider::MAINNET.provider(); let addr = provider.resolve_name("registrar.firefly.eth").await.unwrap(); assert_eq!(addr, "6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()); @@ -1508,7 +1506,7 @@ mod tests { #[tokio::test] // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 async fn mainnet_lookup_address() { - let provider = Provider::::try_from(INFURA).unwrap(); + let provider = crate::MAINNET.provider(); let name = provider .lookup_address("6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()) @@ -1525,7 +1523,7 @@ mod tests { #[tokio::test] async fn mainnet_resolve_avatar() { - let provider = Provider::::try_from(INFURA).unwrap(); + let provider = crate::MAINNET.provider(); for (ens_name, res) in &[ // HTTPS diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs index 3caec3cf..a6ffa2b8 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/provider.rs @@ -1,5 +1,5 @@ #![cfg(not(target_arch = "wasm32"))] -use ethers_providers::{Http, Middleware, Provider}; +use ethers_providers::{Http, Middleware, Provider, RINKEBY}; use std::{convert::TryFrom, time::Duration}; #[cfg(not(feature = "celo"))] @@ -12,10 +12,7 @@ mod eth_tests { #[tokio::test] async fn non_existing_data_works() { - let provider = Provider::::try_from( - "https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); + let provider = RINKEBY.provider(); assert!(provider.get_transaction(H256::zero()).await.unwrap().is_none()); assert!(provider.get_transaction_receipt(H256::zero()).await.unwrap().is_none()); @@ -25,10 +22,7 @@ mod eth_tests { #[tokio::test] async fn client_version() { - let provider = Provider::::try_from( - "https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); + let provider = RINKEBY.provider(); // e.g., Geth/v1.10.6-omnibus-1af33248/linux-amd64/go1.16.6 assert!(provider @@ -95,10 +89,7 @@ mod eth_tests { #[tokio::test] async fn eip1559_fee_estimation() { - let provider = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); + let provider = ethers_providers::MAINNET.provider(); let (_max_fee_per_gas, _max_priority_fee_per_gas) = provider.estimate_eip1559_fees(None).await.unwrap(); From 4f372878599b10ed7021a12e8f3096fb82d154b9 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sun, 13 Mar 2022 10:11:27 -0700 Subject: [PATCH 08/18] feat: rotate infura keys for ws --- ethers-middleware/tests/gas_escalator.rs | 8 +++----- ethers-middleware/tests/signer.rs | 6 +----- ethers-providers/src/lib.rs | 11 ++++++++++- ethers-providers/tests/provider.rs | 6 +----- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/ethers-middleware/tests/gas_escalator.rs b/ethers-middleware/tests/gas_escalator.rs index 9771a7bb..a9eff59c 100644 --- a/ethers-middleware/tests/gas_escalator.rs +++ b/ethers-middleware/tests/gas_escalator.rs @@ -4,7 +4,7 @@ use ethers_middleware::{ gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, signer::SignerMiddleware, }; -use ethers_providers::{Middleware, Provider, Ws}; +use ethers_providers::Middleware; use ethers_signers::{LocalWallet, Signer}; use std::time::Duration; @@ -12,10 +12,8 @@ use std::time::Duration; #[ignore] async fn gas_escalator_live() { // connect to ropsten for getting bad block times - let ws = Ws::connect("wss://ropsten.infura.io/ws/v3/fd8b88b56aa84f6da87b60f5441d6778") - .await - .unwrap(); - let provider = Provider::new(ws).interval(Duration::from_millis(2000u64)); + let provider = ethers_providers::ROPSTEN.ws().await; + let provider = provider.interval(Duration::from_millis(2000u64)); let wallet = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169" .parse::() .unwrap(); diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs index 6ec3fea6..e2cca684 100644 --- a/ethers-middleware/tests/signer.rs +++ b/ethers-middleware/tests/signer.rs @@ -69,11 +69,7 @@ use ethers_core::types::{Address, Eip1559TransactionRequest}; #[tokio::test] #[cfg(not(feature = "celo"))] async fn websocket_pending_txs_with_confirmations_testnet() { - let provider = - Provider::connect("wss://rinkeby.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27") - .await - .unwrap() - .interval(Duration::from_millis(3000)); + let provider = RINKEBY.ws().await.interval(Duration::from_millis(3000)); let chain_id = provider.get_chainid().await.unwrap(); let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); let address = wallet.address(); diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 00e90d9c..77f1298a 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -664,7 +664,7 @@ pub use test_provider::{GOERLI, MAINNET, RINKEBY, ROPSTEN}; /// to prevent rate limits pub mod test_provider { use super::*; - use crate::Http; + use crate::{Http, Ws}; use once_cell::sync::Lazy; use std::{convert::TryFrom, iter::Cycle, slice::Iter, sync::Mutex}; @@ -705,5 +705,14 @@ pub mod test_provider { ); Provider::try_from(url.as_str()).unwrap() } + + pub async fn ws(&self) -> Provider { + let url = format!( + "wss://{}.infura.io/ws/v3/{}", + self.network, + self.keys.lock().unwrap().next().unwrap() + ); + Provider::connect(url.as_str()).await.unwrap() + } } } diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs index a6ffa2b8..54129843 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/provider.rs @@ -35,11 +35,7 @@ mod eth_tests { // Without TLS this would error with "TLS Support not compiled in" #[tokio::test] async fn ssl_websocket() { - use ethers_providers::Ws; - let ws = Ws::connect("wss://rinkeby.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27") - .await - .unwrap(); - let provider = Provider::new(ws); + let provider = RINKEBY.ws().await; let _number = provider.get_block_number().await.unwrap(); } From a02d5a817446477d3978d3bc4c013b5dcdb009d6 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sun, 13 Mar 2022 11:22:41 -0700 Subject: [PATCH 09/18] fix: bump svm-rs for mac m1 solc installs ref: https://github.com/roynalnaruto/svm-rs/pull/29 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cf3431f7..a09f5654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3576,7 +3576,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" version = "0.2.9" -source = "git+https://github.com/roynalnaruto/svm-rs#3aea7a7401bb5cf62e915d42ebdc764172452187" +source = "git+https://github.com/roynalnaruto/svm-rs#ae79a29f5bde08f1991f981456253fa5b6859047" dependencies = [ "anyhow", "cfg-if 1.0.0", From beffe32f15aa00c455722040517d589d81f242a8 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sun, 13 Mar 2022 11:29:09 -0700 Subject: [PATCH 10/18] fix: feature-gate TestProvider.ws fn --- ethers-providers/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 77f1298a..e373e085 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -664,7 +664,7 @@ pub use test_provider::{GOERLI, MAINNET, RINKEBY, ROPSTEN}; /// to prevent rate limits pub mod test_provider { use super::*; - use crate::{Http, Ws}; + use crate::Http; use once_cell::sync::Lazy; use std::{convert::TryFrom, iter::Cycle, slice::Iter, sync::Mutex}; @@ -706,7 +706,8 @@ pub mod test_provider { Provider::try_from(url.as_str()).unwrap() } - pub async fn ws(&self) -> Provider { + #[cfg(feature = "ws")] + pub async fn ws(&self) -> Provider { let url = format!( "wss://{}.infura.io/ws/v3/{}", self.network, From 6b0a1f7911a4d676a1454114b17b3944067e56cd Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 13 Mar 2022 23:36:26 +0100 Subject: [PATCH 11/18] fix(solc): skip artifacts check for files without artifacts (#1018) --- ethers-solc/src/cache.rs | 37 +++++++++++++++------------ ethers-solc/tests/project.rs | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs index 9c0edb8e..3a365e2f 100644 --- a/ethers-solc/src/cache.rs +++ b/ethers-solc/src/cache.rs @@ -638,23 +638,28 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { return true } - if !entry.contains_version(version) { - tracing::trace!( - "missing linked artifacts for source file `{}` for version \"{}\"", - file.display(), - version - ); - return true - } - - if entry.artifacts_for_version(version).any(|artifact_path| { - let missing_artifact = !self.cached_artifacts.has_artifact(artifact_path); - if missing_artifact { - tracing::trace!("missing artifact \"{}\"", artifact_path.display()); + // only check artifact's existence if the file generated artifacts. + // e.g. a solidity file consisting only of import statements (like interfaces that + // re-export) do not create artifacts + if !entry.artifacts.is_empty() { + if !entry.contains_version(version) { + tracing::trace!( + "missing linked artifacts for source file `{}` for version \"{}\"", + file.display(), + version + ); + return true + } + + if entry.artifacts_for_version(version).any(|artifact_path| { + let missing_artifact = !self.cached_artifacts.has_artifact(artifact_path); + if missing_artifact { + tracing::trace!("missing artifact \"{}\"", artifact_path.display()); + } + missing_artifact + }) { + return true } - missing_artifact - }) { - return true } // all things match, can be reused return false diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index c1e7ee28..e2fd432f 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -683,3 +683,51 @@ fn can_recompile_with_changes() { assert!(compiled.find("A").is_some()); assert!(compiled.find("B").is_some()); } + +#[test] +fn can_recompile_unchanged_with_empty_files() { + let tmp = TempProject::dapptools().unwrap(); + + tmp.add_source( + "A", + r#" + pragma solidity ^0.8.10; + import "./B.sol"; + contract A {} + "#, + ) + .unwrap(); + + tmp.add_source( + "B", + r#" + pragma solidity ^0.8.10; + import "./C.sol"; + "#, + ) + .unwrap(); + + let c = r#" + pragma solidity ^0.8.10; + contract C {} + "#; + tmp.add_source("C", c).unwrap(); + + let compiled = tmp.compile().unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(compiled.find("A").is_some()); + assert!(compiled.find("C").is_some()); + + let compiled = tmp.compile().unwrap(); + assert!(compiled.find("A").is_some()); + assert!(compiled.find("C").is_some()); + assert!(compiled.is_unchanged()); + + // modify C.sol + tmp.add_source("C", format!("{}\n", c)).unwrap(); + let compiled = tmp.compile().unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(!compiled.is_unchanged()); + assert!(compiled.find("A").is_some()); + assert!(compiled.find("C").is_some()); +} From f36bc61effc3f237db58fe480556154cc585a5cc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 14 Mar 2022 00:44:01 +0100 Subject: [PATCH 12/18] fix: use correct artifact api (#1019) --- examples/contract_human_readable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/contract_human_readable.rs b/examples/contract_human_readable.rs index 674c2e66..a4315a7c 100644 --- a/examples/contract_human_readable.rs +++ b/examples/contract_human_readable.rs @@ -30,7 +30,7 @@ async fn main() -> Result<()> { let project = Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap(); // compile the project and get the artifacts let output = project.compile().unwrap(); - let contract = output.find("SimpleStorage").expect("could not find contract").into_owned(); + let contract = output.find("SimpleStorage").expect("could not find contract").clone(); let (abi, bytecode, _) = contract.into_parts_or_default(); // 2. instantiate our wallet & ganache From b642230c7df212fedb3e29f0712b4ddadf484bad Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 14 Mar 2022 16:57:06 +0530 Subject: [PATCH 13/18] feat(signer): set from on tx before calling eth_call, eth_createAccessList, eth_estimateGas (#1021) --- ethers-middleware/src/signer.rs | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/ethers-middleware/src/signer.rs b/ethers-middleware/src/signer.rs index 542df8ca..c4b72a2e 100644 --- a/ethers-middleware/src/signer.rs +++ b/ethers-middleware/src/signer.rs @@ -1,5 +1,6 @@ use ethers_core::types::{ - transaction::eip2718::TypedTransaction, Address, BlockId, Bytes, Signature, + transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed}, + Address, BlockId, Bytes, Signature, U256, }; use ethers_providers::{maybe, FromErr, Middleware, PendingTransaction}; use ethers_signers::Signer; @@ -158,6 +159,14 @@ where this.signer = signer; this } + + fn set_tx_from_if_none(&self, tx: &TypedTransaction) -> TypedTransaction { + let mut tx = tx.clone(); + if tx.from().is_none() { + tx.set_from(self.address); + } + tx + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -264,6 +273,32 @@ where ) -> Result { self.signer.sign_message(data.into()).await.map_err(SignerMiddlewareError::SignerError) } + + async fn estimate_gas(&self, tx: &TypedTransaction) -> Result { + let tx = self.set_tx_from_if_none(tx); + self.inner.estimate_gas(&tx).await.map_err(SignerMiddlewareError::MiddlewareError) + } + + async fn create_access_list( + &self, + tx: &TypedTransaction, + block: Option, + ) -> Result { + let tx = self.set_tx_from_if_none(tx); + self.inner + .create_access_list(&tx, block) + .await + .map_err(SignerMiddlewareError::MiddlewareError) + } + + async fn call( + &self, + tx: &TypedTransaction, + block: Option, + ) -> Result { + let tx = self.set_tx_from_if_none(tx); + self.inner().call(&tx, block).await.map_err(SignerMiddlewareError::MiddlewareError) + } } #[cfg(all(test, not(feature = "celo"), not(target_arch = "wasm32")))] From 97c37f187f634d20168a5f641519d740447388fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 13:27:15 +0200 Subject: [PATCH 14/18] chore(deps): bump pretty_assertions from 1.1.0 to 1.2.0 (#1022) Bumps [pretty_assertions](https://github.com/colin-kiegel/rust-pretty-assertions) from 1.1.0 to 1.2.0. - [Release notes](https://github.com/colin-kiegel/rust-pretty-assertions/releases) - [Changelog](https://github.com/colin-kiegel/rust-pretty-assertions/blob/main/CHANGELOG.md) - [Commits](https://github.com/colin-kiegel/rust-pretty-assertions/commits) --- updated-dependencies: - dependency-name: pretty_assertions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- ethers-solc/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a09f5654..d5d0faa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2667,9 +2667,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50" +checksum = "57c038cb5319b9c704bf9c227c261d275bfec0ad438118a2787ce47944fb228b" dependencies = [ "ansi_term", "ctor", diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index 31e3d64b..26b14ca7 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -53,7 +53,7 @@ criterion = { version = "0.3", features = ["async_tokio"] } env_logger = "*" tracing-subscriber = {version = "0.3", default-features = false, features = ["env-filter", "fmt"]} rand = "0.8.5" -pretty_assertions = "1.1.0" +pretty_assertions = "1.2.0" tempfile = "3.3.0" tokio = { version = "1.15.0", features = ["full"] } From 20a01a260d021d61a7564db6925d30bc9862d394 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 14 Mar 2022 12:28:19 +0100 Subject: [PATCH 15/18] fix(solc/etherscan): trim constructor args (#1024) --- ethers-etherscan/src/contract.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ethers-etherscan/src/contract.rs b/ethers-etherscan/src/contract.rs index 3f7885dd..a3d11146 100644 --- a/ethers-etherscan/src/contract.rs +++ b/ethers-etherscan/src/contract.rs @@ -105,6 +105,7 @@ impl VerifyContract { ) -> Self { self.constructor_arguments = constructor_arguments.map(|s| { s.into() + .trim() // TODO is this correct? .trim_start_matches("0x") .to_string() From 26de7936982bb7dbe2fb8626683a74b0aea00933 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 14 Mar 2022 12:29:00 +0100 Subject: [PATCH 16/18] chore(solc): provide remappings on unresolved import message (#1026) --- ethers-solc/src/report.rs | 18 +++++++++++------- ethers-solc/src/resolver/mod.rs | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ethers-solc/src/report.rs b/ethers-solc/src/report.rs index b4b38253..b7f1a1bc 100644 --- a/ethers-solc/src/report.rs +++ b/ethers-solc/src/report.rs @@ -14,7 +14,7 @@ // https://github.com/tokio-rs/tracing/blob/master/tracing-core/src/dispatch.rs -use crate::{CompilerInput, CompilerOutput, Solc}; +use crate::{remappings::Remapping, CompilerInput, CompilerOutput, Solc}; use semver::Version; use std::{ any::{Any, TypeId}, @@ -102,8 +102,8 @@ pub trait Reporter: 'static { /// Invoked before a new [`Solc`] bin was successfully installed fn on_solc_installation_success(&self, _version: &Version) {} - /// Invoked if the import couldn't be resolved - fn on_unresolved_import(&self, _import: &Path) {} + /// Invoked if the import couldn't be resolved with these remappings + fn on_unresolved_import(&self, _import: &Path, _remappings: &[Remapping]) {} /// If `self` is the same type as the provided `TypeId`, returns an untyped /// [`NonNull`] pointer to that type. Otherwise, returns `None`. @@ -166,8 +166,8 @@ pub(crate) fn solc_installation_success(version: &Version) { get_default(|r| r.reporter.on_solc_installation_success(version)); } -pub(crate) fn unresolved_import(import: &Path) { - get_default(|r| r.reporter.on_unresolved_import(import)); +pub(crate) fn unresolved_import(import: &Path, remappings: &[Remapping]) { + get_default(|r| r.reporter.on_unresolved_import(import, remappings)); } fn get_global() -> Option<&'static Report> { @@ -308,8 +308,12 @@ impl Reporter for BasicStdoutReporter { println!("Successfully installed solc {}", version); } - fn on_unresolved_import(&self, import: &Path) { - println!("Unable to resolve imported file: \"{}\"", import.display()); + fn on_unresolved_import(&self, import: &Path, remappings: &[Remapping]) { + println!( + "Unable to resolve import: \"{}\" with remappings:\n {}", + import.display(), + remappings.iter().map(|r| r.to_string()).collect::>().join("\n ") + ); } } diff --git a/ethers-solc/src/resolver/mod.rs b/ethers-solc/src/resolver/mod.rs index a62fc258..f0a70bd7 100644 --- a/ethers-solc/src/resolver/mod.rs +++ b/ethers-solc/src/resolver/mod.rs @@ -274,7 +274,7 @@ impl Graph { add_node(&mut unresolved, &mut index, &mut resolved_imports, import)?; } Err(err) => { - crate::report::unresolved_import(import.data()); + crate::report::unresolved_import(import.data(), &paths.remappings); tracing::trace!("failed to resolve import component \"{:?}\"", err) } }; From 22bc981fd5fe54bab2359319e3ec0706e05a8895 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 14 Mar 2022 12:29:23 +0100 Subject: [PATCH 17/18] chore(abigen): improve error message when bindings out of sync (#1025) --- ethers-contract/ethers-contract-abigen/src/multi.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs index 7d35eef5..e57f10f8 100644 --- a/ethers-contract/ethers-contract-abigen/src/multi.rs +++ b/ethers-contract/ethers-contract-abigen/src/multi.rs @@ -709,8 +709,9 @@ fn check_file_in_dir(dir: &Path, file_name: &str, expected_contents: &[u8]) -> R let file_path = dir.join(file_name); eyre::ensure!(file_path.is_file(), "Not a file: {}", file_path.display()); - let contents = fs::read(file_path).expect("Unable to read file"); - eyre::ensure!(contents == expected_contents, "file contents do not match"); + let contents = fs::read(&file_path).expect("Unable to read file"); + eyre::ensure!(contents == expected_contents, format!("The contents of `{}` do not match the expected output of the newest `ethers::Abigen` version.\ +This indicates that the existing bindings are outdated and need to be generated again.", file_path.display())); Ok(()) } From f6d123241e3a74ab6384598f050c4eb85f0638fc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 14 Mar 2022 12:47:11 +0100 Subject: [PATCH 18/18] feat: add solc install error report (#1027) --- ethers-solc/src/compile/mod.rs | 13 ++++++++++--- ethers-solc/src/remappings.rs | 2 +- ethers-solc/src/report.rs | 14 +++++++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs index 7f4d48db..6bfbac05 100644 --- a/ethers-solc/src/compile/mod.rs +++ b/ethers-solc/src/compile/mod.rs @@ -372,9 +372,16 @@ impl Solc { pub fn blocking_install(version: &Version) -> std::result::Result<(), svm::SolcVmError> { tracing::trace!("blocking installing solc version \"{}\"", version); crate::report::solc_installation_start(version); - svm::blocking_install(version)?; - crate::report::solc_installation_success(version); - Ok(()) + match svm::blocking_install(version) { + Ok(_) => { + crate::report::solc_installation_success(version); + Ok(()) + } + Err(err) => { + crate::report::solc_installation_error(version, &err.to_string()); + Err(err) + } + } } /// Verify that the checksum for this version of solc is correct. We check against the SHA256 diff --git a/ethers-solc/src/remappings.rs b/ethers-solc/src/remappings.rs index 29ec1f57..9ff4c73e 100644 --- a/ethers-solc/src/remappings.rs +++ b/ethers-solc/src/remappings.rs @@ -661,7 +661,7 @@ mod tests { assert_eq!(relative.path.original(), Path::new(&remapping.path)); assert!(relative.path.parent.is_none()); - let relative = RelativeRemapping::new(remapping.clone(), "/a/b"); + let relative = RelativeRemapping::new(remapping, "/a/b"); assert_eq!(relative.to_relative_remapping(), Remapping::from_str("oz/=c/d/").unwrap()); } diff --git a/ethers-solc/src/report.rs b/ethers-solc/src/report.rs index b7f1a1bc..d7806ca8 100644 --- a/ethers-solc/src/report.rs +++ b/ethers-solc/src/report.rs @@ -99,9 +99,12 @@ pub trait Reporter: 'static { /// Invoked before a new [`Solc`] bin is installed fn on_solc_installation_start(&self, _version: &Version) {} - /// Invoked before a new [`Solc`] bin was successfully installed + /// Invoked after a new [`Solc`] bin was successfully installed fn on_solc_installation_success(&self, _version: &Version) {} + /// Invoked after a [`Solc`] installation failed + fn on_solc_installation_error(&self, _version: &Version, _error: &str) {} + /// Invoked if the import couldn't be resolved with these remappings fn on_unresolved_import(&self, _import: &Path, _remappings: &[Remapping]) {} @@ -166,6 +169,11 @@ pub(crate) fn solc_installation_success(version: &Version) { get_default(|r| r.reporter.on_solc_installation_success(version)); } +#[allow(unused)] +pub(crate) fn solc_installation_error(version: &Version, error: &str) { + get_default(|r| r.reporter.on_solc_installation_error(version, error)); +} + pub(crate) fn unresolved_import(import: &Path, remappings: &[Remapping]) { get_default(|r| r.reporter.on_unresolved_import(import, remappings)); } @@ -308,6 +316,10 @@ impl Reporter for BasicStdoutReporter { println!("Successfully installed solc {}", version); } + fn on_solc_installation_error(&self, version: &Version, error: &str) { + eprintln!("Failed to install solc {}: {}", version, error); + } + fn on_unresolved_import(&self, import: &Path, remappings: &[Remapping]) { println!( "Unable to resolve import: \"{}\" with remappings:\n {}",