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).
|
||||
- Generate correct bindings of struct's field names that are reserved words
|
||||
[#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
|
||||
|
||||
|
|
|
@ -41,4 +41,4 @@ rustls = ["reqwest/rustls-tls"]
|
|||
|
||||
[dev-dependencies]
|
||||
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
|
||||
pub fn expand(&self) -> Result<ExpandedContract> {
|
||||
let name = &self.contract_ident;
|
||||
let name_mod =
|
||||
util::ident(&format!("{}_mod", self.contract_ident.to_string().to_lowercase()));
|
||||
let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
|
||||
let abi_name = self.inline_abi_ident();
|
||||
|
||||
// 0. Imports
|
||||
|
|
|
@ -29,7 +29,6 @@ pub use util::parse_address;
|
|||
|
||||
use crate::contract::ExpandedContract;
|
||||
use eyre::Result;
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::TokenStream;
|
||||
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)
|
||||
pub fn module_name(&self) -> String {
|
||||
self.name.to_snake_case()
|
||||
util::safe_module_name(&self.name)
|
||||
}
|
||||
|
||||
/// 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`
|
||||
fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
|
||||
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 {
|
||||
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 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
|
||||
pub fn safe_snake_case_ident(name: &str) -> Ident {
|
||||
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]);
|
||||
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
|
||||
use ethers_providers::Middleware;
|
||||
// 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 = TypedTransaction::Eip1559(tx);
|
||||
let res5 = client.call(&tx, None).await.unwrap();
|
||||
|
|
|
@ -601,7 +601,7 @@ mod eth_tests {
|
|||
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,
|
||||
bar: foo_bar.bar,
|
||||
fizz: foo_bar.fizz.clone(),
|
||||
|
|
Loading…
Reference in New Issue