diff --git a/CHANGELOG.md b/CHANGELOG.md index e1075075..c9676bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,7 +101,7 @@ ## ethers-contract-abigen ### Unreleased - +- Fix Cargo.toml generation issue that could cause dependency conflicts [#1852](https://github.com/gakonst/ethers-rs/pull/1852) - Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674) - Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564) - generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549) diff --git a/Cargo.lock b/Cargo.lock index df7334c4..a848202b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1278,6 +1278,7 @@ dependencies = [ "serde_json", "syn", "tempfile", + "toml", "url", "walkdir", ] @@ -4100,9 +4101,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml index f57fd771..ec96bbae 100644 --- a/ethers-contract/ethers-contract-abigen/Cargo.toml +++ b/ethers-contract/ethers-contract-abigen/Cargo.toml @@ -29,6 +29,7 @@ dunce = "1.0.2" walkdir = "2.3.2" eyre = "0.6" regex = "1.6.0" +toml = "0.5.9" [target.'cfg(target_arch = "wasm32")'.dependencies] # NOTE: this enables wasm compatibility for getrandom indirectly diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs index b09a7019..10c3e3c9 100644 --- a/ethers-contract/ethers-contract-abigen/src/multi.rs +++ b/ethers-contract/ethers-contract-abigen/src/multi.rs @@ -1,4 +1,5 @@ //! Generate bindings for multiple `Abigen` +use crate::{util, Abigen, Context, ContractBindings, ContractFilter, ExpandedContract}; use eyre::Result; use inflector::Inflector; use proc_macro2::TokenStream; @@ -7,10 +8,9 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, fs, io::Write, - path::Path, + path::{Path, PathBuf}, }; - -use crate::{util, Abigen, Context, ContractBindings, ContractFilter, ExpandedContract}; +use toml::Value; /// Collects Abigen structs for a series of contracts, pending generation of /// the contract bindings. @@ -553,6 +553,7 @@ impl MultiBindingsInner { &self, name: impl AsRef, version: impl AsRef, + crate_version: String, ) -> Result> { let mut toml = vec![]; @@ -564,15 +565,31 @@ impl MultiBindingsInner { writeln!(toml, "# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html")?; writeln!(toml)?; writeln!(toml, "[dependencies]")?; - writeln!( - toml, - r#" -ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["abigen"] }} -"# - )?; + writeln!(toml, r#"{}"#, crate_version)?; Ok(toml) } + /// parses the active Cargo.toml to get what version of ethers we are using + fn find_crate_version(&self) -> Result { + let cargo_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml"); + let data = std::fs::read_to_string(cargo_dir)?; + let toml = data.parse::()?; + + let Some(ethers) = toml.get("dependencies") + .and_then (|v| v.get("ethers").or_else(|| v.get("ethers-contract"))) + else { eyre::bail!("couldn't find ethers or ethers-contract dependency")}; + if let Some(rev) = ethers.get("rev") { + Ok(format!("ethers = {{ git = \"https://github.com/gakonst/ethers-rs\", rev = {}, default-features = false, features = [\"abigen\"] }}", rev)) + } else if let Some(version) = ethers.get("version") { + Ok(format!( + "ethers = {{ version = {}, default-features = false, features = [\"abigen\"] }}", + version + )) + } else { + Ok("ethers = {{ git = \"https://github.com/gakonst/ethers-rs\", default-features = false, features = [\"abigen\"] }}".to_string()) + } + } + /// Write the contents of `Cargo.toml` to disk fn write_cargo_toml( &self, @@ -580,7 +597,8 @@ ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = fal name: impl AsRef, version: impl AsRef, ) -> Result<()> { - let contents = self.generate_cargo_toml(name, version)?; + let crate_version = self.find_crate_version()?; + let contents = self.generate_cargo_toml(name, version, crate_version)?; let mut file = fs::OpenOptions::new() .read(true) @@ -717,7 +735,8 @@ ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = fal if check_cargo_toml { // additionally check the contents of the cargo - let cargo_contents = self.generate_cargo_toml(name, version)?; + let crate_version = self.find_crate_version()?; + let cargo_contents = self.generate_cargo_toml(name, version, crate_version)?; check_file_in_dir(crate_path, "Cargo.toml", &cargo_contents)?; } @@ -775,7 +794,7 @@ mod tests { use crate::{ExcludeContracts, SelectContracts}; use ethers_solc::project_util::TempProject; - use std::{panic, path::PathBuf}; + use std::{env, panic, path::PathBuf}; struct Context { multi_gen: MultiAbigen, @@ -1238,4 +1257,195 @@ contract Enum { let content = fs::read_to_string(&mod_).unwrap(); assert!(content.contains("pub mod mod_ {")); } + + #[test] + fn parse_ethers_crate() { + // gotta bunch these all together as we are overwriting env vars + run_test(|context| { + let Context { multi_gen, mod_root } = context; + let tmp = TempProject::dapptools().unwrap(); + + tmp.add_source( + "Cargo.toml", + r#" + [package] + name = "ethers-contract" + version = "1.0.0" + edition = "2018" + rust-version = "1.62" + authors = ["Georgios Konstantopoulos "] + license = "MIT OR Apache-2.0" + description = "Smart contract bindings for the ethers-rs crate" + homepage = "https://docs.rs/ethers" + repository = "https://github.com/gakonst/ethers-rs" + keywords = ["ethereum", "web3", "celo", "ethers"] + + [dependencies] + ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false } +"#, + ) + .unwrap(); + + let _ = tmp.compile().unwrap(); + env::set_var("CARGO_MANIFEST_DIR", tmp.root()); + let single_file = false; + let name = "a-name"; + let version = "290.3782.3"; + + multi_gen + .clone() + .build() + .unwrap() + .write_to_crate(name, version, mod_root, single_file) + .unwrap(); + + multi_gen + .clone() + .build() + .unwrap() + .ensure_consistent_crate(name, version, mod_root, single_file, true) + .expect("Inconsistent bindings"); + }); + + run_test(|context| { + let Context { multi_gen, mod_root } = context; + let tmp = TempProject::dapptools().unwrap(); + + tmp.add_source( + "Cargo.toml", + r#" + [package] + name = "ethers-contract" + version = "1.0.0" + edition = "2018" + rust-version = "1.62" + authors = ["Georgios Konstantopoulos "] + license = "MIT OR Apache-2.0" + description = "Smart contract bindings for the ethers-rs crate" + homepage = "https://docs.rs/ethers" + repository = "https://github.com/gakonst/ethers-rs" + keywords = ["ethereum", "web3", "celo", "ethers"] + + [dependencies] + ethers-contracts = "0.4.0" +"#, + ) + .unwrap(); + + let _ = tmp.compile().unwrap(); + env::set_var("CARGO_MANIFEST_DIR", tmp.root()); + + let single_file = false; + let name = "a-name"; + let version = "290.3782.3"; + + multi_gen + .clone() + .build() + .unwrap() + .write_to_crate(name, version, mod_root, single_file) + .unwrap(); + + multi_gen + .clone() + .build() + .unwrap() + .ensure_consistent_crate(name, version, mod_root, single_file, true) + .expect("Inconsistent bindings"); + }); + + run_test(|context| { + let Context { multi_gen, mod_root } = context; + let tmp = TempProject::dapptools().unwrap(); + + tmp.add_source( + "Cargo.toml", + r#" + [package] + name = "ethers-contract" + version = "1.0.0" + edition = "2018" + rust-version = "1.62" + authors = ["Georgios Konstantopoulos "] + license = "MIT OR Apache-2.0" + description = "Smart contract bindings for the ethers-rs crate" + homepage = "https://docs.rs/ethers" + repository = "https://github.com/gakonst/ethers-rs" + keywords = ["ethereum", "web3", "celo", "ethers"] + + [dependencies] + ethers = {git="https://github.com/gakonst/ethers-rs", rev = "fd8ebf5",features = ["ws", "rustls", "ipc"] } +"#, + ) + .unwrap(); + + let _ = tmp.compile().unwrap(); + env::set_var("CARGO_MANIFEST_DIR", tmp.root()); + + let single_file = false; + let name = "a-name"; + let version = "290.3782.3"; + + multi_gen + .clone() + .build() + .unwrap() + .write_to_crate(name, version, mod_root, single_file) + .unwrap(); + + multi_gen + .clone() + .build() + .unwrap() + .ensure_consistent_crate(name, version, mod_root, single_file, true) + .expect("Inconsistent bindings"); + }); + + run_test(|context| { + let Context { multi_gen, mod_root } = context; + let tmp = TempProject::dapptools().unwrap(); + + tmp.add_source( + "Cargo.toml", + r#" + [package] + name = "ethers-contract" + version = "1.0.0" + edition = "2018" + rust-version = "1.62" + authors = ["Georgios Konstantopoulos "] + license = "MIT OR Apache-2.0" + description = "Smart contract bindings for the ethers-rs crate" + homepage = "https://docs.rs/ethers" + repository = "https://github.com/gakonst/ethers-rs" + keywords = ["ethereum", "web3", "celo", "ethers"] + + [dependencies] + ethers = {git="https://github.com/gakonst/ethers-rs" ,features = ["ws", "rustls", "ipc"] } +"#, + ) + .unwrap(); + + let _ = tmp.compile().unwrap(); + env::set_var("CARGO_MANIFEST_DIR", tmp.root()); + + let single_file = false; + let name = "a-name"; + let version = "290.3782.3"; + + multi_gen + .clone() + .build() + .unwrap() + .write_to_crate(name, version, mod_root, single_file) + .unwrap(); + + multi_gen + .clone() + .build() + .unwrap() + .ensure_consistent_crate(name, version, mod_root, single_file, true) + .expect("Inconsistent bindings"); + }); + } }