Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

fix: generated crate not using generated version #1852

Merged
merged 14 commits into from
Nov 15, 2022
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,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)
Expand Down
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ethers-contract/ethers-contract-abigen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
234 changes: 223 additions & 11 deletions ethers-contract/ethers-contract-abigen/src/multi.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,8 +10,7 @@ use std::{
io::Write,
path::Path,
};

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.
Expand Down Expand Up @@ -553,6 +553,7 @@ impl MultiBindingsInner {
&self,
name: impl AsRef<str>,
version: impl AsRef<str>,
crate_version: String,
) -> Result<Vec<u8>> {
let mut toml = vec![];

Expand All @@ -564,23 +565,42 @@ 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<String> {
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::<Value>()?;

let Some(Some(ethers)) = toml.get("dependencies").map(|v| v.get("ethers")) else { eyre::bail!("couldn't find ethers dep")};
Will-Smith11 marked this conversation as resolved.
Show resolved Hide resolved
if ethers.is_table() {
if let Some(rev) = ethers.get("rev") {
return 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") {
return Ok(format!("ethers = {{ version = {}, default-features = false, features = [\"abigen\"] }}", version ));
} else {
return Ok("ethers = {{ git = \"https://github.com/gakonst/ethers-rs\", default-features = false, features = [\"abigen\"] }}".to_string());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure this is correct, since version should also be allowed with path.

I think we can simplify all of this by checking for path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you say path I'm assuming we are talking about something like this ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false } ? I also see another case as if someone was just importing ethers-contracts this would also miss out on it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes the path argument.

importing ethers-contracts this would also miss out on it.

right, let's check for both ethers and ethers_contract then?

}
} else {
return Ok(format!(
"ethers = {{ version={}, default-features = false, features = [\"abigen\"] }}",
ethers
))
}
}

/// Write the contents of `Cargo.toml` to disk
fn write_cargo_toml(
&self,
lib: &Path,
name: impl AsRef<str>,
version: impl AsRef<str>,
) -> 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)
Expand Down Expand Up @@ -717,7 +737,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)?;
}

Expand Down Expand Up @@ -775,7 +796,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,
Expand Down Expand Up @@ -1238,4 +1259,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 <me@gakonst.com>"]
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 <me@gakonst.com>"]
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 <me@gakonst.com>"]
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 <me@gakonst.com>"]
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");
});
}
}