fix(abigen): contract names can be reserve words (#1498)
* fix(abigen): contract names can be reserve words * update changelog * clippy warning * module names consistent with other safe_* * update refs to generated abis * move reserved words tests to their own fn * added note to changelog re: module name changes
This commit is contained in:
parent
a600acb4f5
commit
aa008727ee
|
@ -90,6 +90,10 @@
|
||||||
[#1030](https://github.com/gakonst/ethers-rs/pull/1030).
|
[#1030](https://github.com/gakonst/ethers-rs/pull/1030).
|
||||||
- Generate correct bindings of struct's field names that are reserved words
|
- Generate correct bindings of struct's field names that are reserved words
|
||||||
[#989](https://github.com/gakonst/ethers-rs/pull/989).
|
[#989](https://github.com/gakonst/ethers-rs/pull/989).
|
||||||
|
- Generate correct binding module names that are reserved words
|
||||||
|
[#1498](https://github.com/gakonst/ethers-rs/pull/1498). Note: this changes
|
||||||
|
generated module names to snake case. For example, `MyContract` is now
|
||||||
|
`my_contract` rather than `mycontract_mod`.
|
||||||
|
|
||||||
### 0.6.0
|
### 0.6.0
|
||||||
|
|
||||||
|
|
|
@ -41,4 +41,4 @@ rustls = ["reqwest/rustls-tls"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util"] }
|
ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util", "svm-solc"] }
|
||||||
|
|
|
@ -101,8 +101,7 @@ impl Context {
|
||||||
/// Expands the whole rust contract
|
/// Expands the whole rust contract
|
||||||
pub fn expand(&self) -> Result<ExpandedContract> {
|
pub fn expand(&self) -> Result<ExpandedContract> {
|
||||||
let name = &self.contract_ident;
|
let name = &self.contract_ident;
|
||||||
let name_mod =
|
let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
|
||||||
util::ident(&format!("{}_mod", self.contract_ident.to_string().to_lowercase()));
|
|
||||||
let abi_name = self.inline_abi_ident();
|
let abi_name = self.inline_abi_ident();
|
||||||
|
|
||||||
// 0. Imports
|
// 0. Imports
|
||||||
|
|
|
@ -29,7 +29,6 @@ pub use util::parse_address;
|
||||||
|
|
||||||
use crate::contract::ExpandedContract;
|
use crate::contract::ExpandedContract;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use inflector::Inflector;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use std::{collections::HashMap, fs::File, io::Write, path::Path};
|
use std::{collections::HashMap, fs::File, io::Write, path::Path};
|
||||||
|
|
||||||
|
@ -233,7 +232,7 @@ impl ContractBindings {
|
||||||
|
|
||||||
/// Generate the default module name (snake case of the contract name)
|
/// Generate the default module name (snake case of the contract name)
|
||||||
pub fn module_name(&self) -> String {
|
pub fn module_name(&self) -> String {
|
||||||
self.name.to_snake_case()
|
util::safe_module_name(&self.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the default filename of the module
|
/// Generate the default filename of the module
|
||||||
|
|
|
@ -559,7 +559,7 @@ serde_json = "1.0.79"
|
||||||
/// Append module declarations to the `lib.rs` or `mod.rs`
|
/// Append module declarations to the `lib.rs` or `mod.rs`
|
||||||
fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
|
fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
|
||||||
let mut mod_names: BTreeSet<_> =
|
let mut mod_names: BTreeSet<_> =
|
||||||
self.bindings.keys().map(|name| name.to_snake_case()).collect();
|
self.bindings.keys().map(|name| util::safe_module_name(name)).collect();
|
||||||
if let Some(ref shared) = self.shared_types {
|
if let Some(ref shared) = self.shared_types {
|
||||||
mod_names.insert(shared.name.to_snake_case());
|
mod_names.insert(shared.name.to_snake_case());
|
||||||
}
|
}
|
||||||
|
@ -1093,4 +1093,63 @@ contract Greeter2 {
|
||||||
assert!(content.contains("pub struct Inner"));
|
assert!(content.contains("pub struct Inner"));
|
||||||
assert!(content.contains("pub struct Stuff"));
|
assert!(content.contains("pub struct Stuff"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_sanitize_reserved_words() {
|
||||||
|
let tmp = TempProject::dapptools().unwrap();
|
||||||
|
|
||||||
|
tmp.add_source(
|
||||||
|
"ReservedWords",
|
||||||
|
r#"
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity >=0.8.0;
|
||||||
|
|
||||||
|
contract Mod {
|
||||||
|
function greet() public pure returns (uint256) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from a gnosis contract
|
||||||
|
contract Enum {
|
||||||
|
enum Operation {Call, DelegateCall}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _ = tmp.compile().unwrap();
|
||||||
|
|
||||||
|
let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
|
||||||
|
let bindings = gen.build().unwrap();
|
||||||
|
let single_file_dir = tmp.root().join("single_bindings");
|
||||||
|
bindings.write_to_module(&single_file_dir, true).unwrap();
|
||||||
|
|
||||||
|
let single_file_mod = single_file_dir.join("mod.rs");
|
||||||
|
assert!(single_file_mod.exists());
|
||||||
|
let content = fs::read_to_string(&single_file_mod).unwrap();
|
||||||
|
assert!(content.contains("pub mod mod_ {"));
|
||||||
|
assert!(content.contains("pub mod enum_ {"));
|
||||||
|
|
||||||
|
// multiple files
|
||||||
|
let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
|
||||||
|
let bindings = gen.build().unwrap();
|
||||||
|
let multi_file_dir = tmp.root().join("multi_bindings");
|
||||||
|
bindings.write_to_module(&multi_file_dir, false).unwrap();
|
||||||
|
let multi_file_mod = multi_file_dir.join("mod.rs");
|
||||||
|
assert!(multi_file_mod.exists());
|
||||||
|
let content = fs::read_to_string(&multi_file_mod).unwrap();
|
||||||
|
assert!(content.contains("pub mod enum_;"));
|
||||||
|
assert!(content.contains("pub mod mod_;"));
|
||||||
|
|
||||||
|
let enum_ = multi_file_dir.join("enum_.rs");
|
||||||
|
assert!(enum_.exists());
|
||||||
|
let content = fs::read_to_string(&enum_).unwrap();
|
||||||
|
assert!(content.contains("pub mod enum_ {"));
|
||||||
|
|
||||||
|
let mod_ = multi_file_dir.join("mod_.rs");
|
||||||
|
assert!(mod_.exists());
|
||||||
|
let content = fs::read_to_string(&mod_).unwrap();
|
||||||
|
assert!(content.contains("pub mod mod_ {"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,12 @@ fn safe_identifier_name(name: String) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// converts invalid rust module names to valid ones
|
||||||
|
pub fn safe_module_name(name: &str) -> String {
|
||||||
|
// handle reserve words used in contracts (eg Enum is a gnosis contract)
|
||||||
|
safe_ident(&safe_snake_case(name)).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Expands an identifier as snakecase and preserve any leading or trailing underscores
|
/// Expands an identifier as snakecase and preserve any leading or trailing underscores
|
||||||
pub fn safe_snake_case_ident(name: &str) -> Ident {
|
pub fn safe_snake_case_ident(name: &str) -> Ident {
|
||||||
let i = name.to_snake_case();
|
let i = name.to_snake_case();
|
||||||
|
@ -228,4 +234,12 @@ mod tests {
|
||||||
Address::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
Address::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||||
assert_eq!(parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(), expected);
|
assert_eq!(parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_safe_module_name() {
|
||||||
|
assert_eq!(safe_module_name("Valid"), "valid");
|
||||||
|
assert_eq!(safe_module_name("Enum"), "enum_");
|
||||||
|
assert_eq!(safe_module_name("Mod"), "mod_");
|
||||||
|
assert_eq!(safe_module_name("2Two"), "_2_two");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,7 +366,7 @@ async fn can_handle_underscore_functions() {
|
||||||
// Manual call construction
|
// Manual call construction
|
||||||
use ethers_providers::Middleware;
|
use ethers_providers::Middleware;
|
||||||
// TODO: How do we handle underscores for calls here?
|
// TODO: How do we handle underscores for calls here?
|
||||||
let data = simplestorage_mod::HashPuzzleCall.encode();
|
let data = simple_storage::HashPuzzleCall.encode();
|
||||||
let tx = Eip1559TransactionRequest::new().data(data).to(addr);
|
let tx = Eip1559TransactionRequest::new().data(data).to(addr);
|
||||||
let tx = TypedTransaction::Eip1559(tx);
|
let tx = TypedTransaction::Eip1559(tx);
|
||||||
let res5 = client.call(&tx, None).await.unwrap();
|
let res5 = client.call(&tx, None).await.unwrap();
|
||||||
|
|
|
@ -601,7 +601,7 @@ mod eth_tests {
|
||||||
out: Address::from([0; 20]),
|
out: Address::from([0; 20]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let derived_foo_bar = deriveeip712test_mod::FooBar {
|
let derived_foo_bar = derive_eip_712_test::FooBar {
|
||||||
foo: foo_bar.foo,
|
foo: foo_bar.foo,
|
||||||
bar: foo_bar.bar,
|
bar: foo_bar.bar,
|
||||||
fizz: foo_bar.fizz.clone(),
|
fizz: foo_bar.fizz.clone(),
|
||||||
|
|
Loading…
Reference in New Issue