Event aliasing for contract bindings (#425)
* contracts: enable event aliases for Abigen * contract: unit tests for event aliases * contract: cleanup expand_event function * Address pr suggestions * contracts: remove unnecessary clone Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> * Make clippy happy Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
parent
520645c48b
commit
32ad5a6abd
|
@ -46,6 +46,9 @@ pub(crate) struct Context {
|
|||
|
||||
/// Derives added to event structs and enums.
|
||||
event_derives: Vec<Path>,
|
||||
|
||||
/// Manually specified event aliases.
|
||||
event_aliases: BTreeMap<String, Ident>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
@ -151,6 +154,12 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
let mut event_aliases = BTreeMap::new();
|
||||
for (signature, alias) in args.event_aliases.into_iter() {
|
||||
let alias = syn::parse_str(&alias)?;
|
||||
event_aliases.insert(signature, alias);
|
||||
}
|
||||
|
||||
let event_derives = args
|
||||
.event_derives
|
||||
.iter()
|
||||
|
@ -167,6 +176,7 @@ impl Context {
|
|||
contract_name,
|
||||
method_aliases,
|
||||
event_derives,
|
||||
event_aliases,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ impl Context {
|
|||
let variants = sorted_events
|
||||
.values()
|
||||
.flatten()
|
||||
.map(expand_struct_name)
|
||||
.map(|e| expand_struct_name(e, self.event_aliases.get(&e.abi_signature()).cloned()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let enum_name = self.expand_event_enum_name();
|
||||
|
@ -124,7 +124,10 @@ impl Context {
|
|||
let ty = if iter.next().is_some() {
|
||||
self.expand_event_enum_name()
|
||||
} else {
|
||||
expand_struct_name(event)
|
||||
expand_struct_name(
|
||||
event,
|
||||
self.event_aliases.get(&event.abi_signature()).cloned(),
|
||||
)
|
||||
};
|
||||
|
||||
quote! {
|
||||
|
@ -220,12 +223,18 @@ impl Context {
|
|||
/// Expands into a single method for contracting an event stream.
|
||||
fn expand_filter(&self, event: &Event) -> TokenStream {
|
||||
let ethers_contract = util::ethers_contract_crate();
|
||||
let alias = self.event_aliases.get(&event.abi_signature()).cloned();
|
||||
|
||||
let name = if let Some(id) = alias.clone() {
|
||||
util::safe_ident(&format!("{}_filter", id.to_string().to_snake_case()))
|
||||
} else {
|
||||
util::safe_ident(&format!("{}_filter", event.name.to_snake_case()))
|
||||
};
|
||||
|
||||
// append `filter` to disambiguate with potentially conflicting
|
||||
// function names
|
||||
let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case()));
|
||||
// let result = util::ident(&event.name.to_pascal_case());
|
||||
let result = expand_struct_name(event);
|
||||
|
||||
let result = expand_struct_name(event, alias);
|
||||
|
||||
let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
|
||||
quote! {
|
||||
|
@ -240,7 +249,11 @@ impl Context {
|
|||
/// into a structure or a tuple in the case where all event parameters (topics
|
||||
/// and data) are anonymous.
|
||||
fn expand_event(&self, event: &Event) -> Result<TokenStream> {
|
||||
let event_name = expand_struct_name(event);
|
||||
let sig = self.event_aliases.get(&event.abi_signature()).cloned();
|
||||
let abi_signature = event.abi_signature();
|
||||
let event_abi_name = event.name.clone();
|
||||
|
||||
let event_name = expand_struct_name(event, sig);
|
||||
|
||||
let params = self.expand_params(event)?;
|
||||
// expand as a tuple if all fields are anonymous
|
||||
|
@ -252,8 +265,6 @@ impl Context {
|
|||
};
|
||||
|
||||
let derives = expand_derives(&self.event_derives);
|
||||
let abi_signature = event.abi_signature();
|
||||
let event_abi_name = &event.name;
|
||||
|
||||
let ethers_contract = util::ethers_contract_crate();
|
||||
|
||||
|
@ -309,9 +320,14 @@ impl Context {
|
|||
}
|
||||
|
||||
/// Expands an ABI event into an identifier for its event data type.
|
||||
fn expand_struct_name(event: &Event) -> Ident {
|
||||
fn expand_struct_name(event: &Event, alias: Option<Ident>) -> Ident {
|
||||
// TODO: get rid of `Filter` suffix?
|
||||
let name = format!("{}Filter", event.name.to_pascal_case());
|
||||
|
||||
let name = if let Some(id) = alias {
|
||||
format!("{}Filter", id.to_string().to_pascal_case())
|
||||
} else {
|
||||
format!("{}Filter", event.name.to_pascal_case())
|
||||
};
|
||||
util::ident(&name)
|
||||
}
|
||||
|
||||
|
@ -378,6 +394,50 @@ mod tests {
|
|||
Context::from_abigen(Abigen::new("TestToken", "[]").unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn test_context_with_alias(sig: &str, alias: &str) -> Context {
|
||||
Context::from_abigen(
|
||||
Abigen::new("TestToken", "[]")
|
||||
.unwrap()
|
||||
.add_event_alias(sig, alias),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn expand_transfer_filter_with_alias() {
|
||||
let event = Event {
|
||||
name: "Transfer".into(),
|
||||
inputs: vec![
|
||||
EventParam {
|
||||
name: "from".into(),
|
||||
kind: ParamType::Address,
|
||||
indexed: true,
|
||||
},
|
||||
EventParam {
|
||||
name: "to".into(),
|
||||
kind: ParamType::Address,
|
||||
indexed: true,
|
||||
},
|
||||
EventParam {
|
||||
name: "amount".into(),
|
||||
kind: ParamType::Uint(256),
|
||||
indexed: false,
|
||||
},
|
||||
],
|
||||
anonymous: false,
|
||||
};
|
||||
let sig = "Transfer(address,address,uint256)";
|
||||
let cx = test_context_with_alias(sig, "TransferEvent");
|
||||
assert_quote!(cx.expand_filter(&event), {
|
||||
#[doc = "Gets the contract's `Transfer` event"]
|
||||
pub fn transfer_event_filter(
|
||||
&self
|
||||
) -> ethers_contract::builders::Event<M, TransferEventFilter> {
|
||||
self.0.event()
|
||||
}
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn expand_transfer_filter() {
|
||||
let event = Event {
|
||||
|
@ -431,7 +491,7 @@ mod tests {
|
|||
|
||||
let cx = test_context();
|
||||
let params = cx.expand_params(&event).unwrap();
|
||||
let name = expand_struct_name(&event);
|
||||
let name = expand_struct_name(&event, None);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
|
||||
assert_quote!(definition, {
|
||||
|
@ -442,6 +502,39 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_data_struct_with_alias() {
|
||||
let event = Event {
|
||||
name: "Foo".into(),
|
||||
inputs: vec![
|
||||
EventParam {
|
||||
name: "a".into(),
|
||||
kind: ParamType::Bool,
|
||||
indexed: false,
|
||||
},
|
||||
EventParam {
|
||||
name: String::new(),
|
||||
kind: ParamType::Address,
|
||||
indexed: false,
|
||||
},
|
||||
],
|
||||
anonymous: false,
|
||||
};
|
||||
|
||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||
let params = cx.expand_params(&event).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = expand_struct_name(&event, alias);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooAliasedFilter {
|
||||
pub a: bool,
|
||||
pub p1: ethers_core::types::Address,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_data_tuple_value() {
|
||||
let event = Event {
|
||||
|
@ -463,7 +556,7 @@ mod tests {
|
|||
|
||||
let cx = test_context();
|
||||
let params = cx.expand_params(&event).unwrap();
|
||||
let name = expand_struct_name(&event);
|
||||
let name = expand_struct_name(&event, None);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
|
||||
assert_quote!(definition, {
|
||||
|
@ -471,6 +564,36 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_data_tuple_value_with_alias() {
|
||||
let event = Event {
|
||||
name: "Foo".into(),
|
||||
inputs: vec![
|
||||
EventParam {
|
||||
name: String::new(),
|
||||
kind: ParamType::Bool,
|
||||
indexed: false,
|
||||
},
|
||||
EventParam {
|
||||
name: String::new(),
|
||||
kind: ParamType::Address,
|
||||
indexed: false,
|
||||
},
|
||||
],
|
||||
anonymous: false,
|
||||
};
|
||||
|
||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||
let params = cx.expand_params(&event).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = expand_struct_name(&event, alias);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
|
||||
assert_quote!(definition, {
|
||||
struct FooAliasedFilter(pub bool, pub ethers_core::types::Address);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn expand_hash_value() {
|
||||
|
|
|
@ -61,6 +61,9 @@ pub struct Abigen {
|
|||
|
||||
/// Format the code using a locally installed copy of `rustfmt`.
|
||||
rustfmt: bool,
|
||||
|
||||
/// Manually specified event name aliases.
|
||||
event_aliases: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Abigen {
|
||||
|
@ -72,10 +75,22 @@ impl Abigen {
|
|||
contract_name: contract_name.to_owned(),
|
||||
method_aliases: HashMap::new(),
|
||||
event_derives: Vec::new(),
|
||||
event_aliases: HashMap::new(),
|
||||
rustfmt: true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Manually adds a solidity event alias to specify what the event struct
|
||||
/// and function name will be in Rust.
|
||||
pub fn add_event_alias<S1, S2>(mut self, signature: S1, alias: S2) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
{
|
||||
self.event_aliases.insert(signature.into(), alias.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Manually adds a solidity method alias to specify what the method name
|
||||
/// will be in Rust. For solidity methods without an alias, the snake cased
|
||||
/// method name will be used.
|
||||
|
|
Loading…
Reference in New Issue