fix: make EthEvent abi attribute work for tuple inputs (#229)

* fix: try to parse the provided abi as tuple

* test: validate ethevent abi attribute

* docs: document ethevent abi attribute
This commit is contained in:
Matthias Seitz 2021-03-16 09:12:32 +01:00 committed by GitHub
parent 7b10b76e20
commit 0c18f9b32c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 106 additions and 36 deletions

View File

@ -13,7 +13,7 @@ use syn::{
};
use abigen::{expand, ContractArgs};
use ethers_core::abi::{AbiParser, Event, EventExt, EventParam, ParamType};
use ethers_core::abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType};
use hex::FromHex;
use spanned::Spanned;
@ -79,9 +79,29 @@ pub fn abigen(input: TokenStream) -> TokenStream {
///
/// Additional arguments can be specified using the `#[ethevent(...)]` attribute:
///
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default is the struct's name.
/// - `signature`, `signature = "..."`: The signature as hex string to override the event's signature.
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default is the
/// struct's name.
/// - `signature`, `signature = "..."`: The signature as hex string to override the
/// event's signature.
/// - `abi`, `abi = "..."`: The ABI signature for the event this event's data corresponds to.
/// The `abi` should be solidity event definition or a tuple of the event's types in case the
/// event has non elementary (other `EthAbiType`) types as members
///
/// # Example
/// ```ignore
/// #[derive(Debug, EthAbiType)]
/// struct Inner {
/// inner: Address,
/// msg: String,
/// }
///
/// #[derive(Debug, EthEvent)]
/// #[ethevent(abi = "ValueChangedEvent((address,string),string)")]
/// struct ValueChangedEvent {
/// inner: Inner,
/// msg: String,
/// }
/// ```
#[proc_macro_derive(EthEvent, attributes(ethevent))]
pub fn derive_abi_event(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
@ -97,28 +117,57 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream {
.unwrap_or_else(|| input.ident.to_string());
let (abi, hash) = if let Some((src, span)) = attributes.abi {
// try to parse as solidity event
if let Ok(mut event) = parse_event(&src) {
event.name = event_name.clone();
(event.abi_signature(), event.signature())
} else {
match src.parse::<Source>().and_then(|s| s.get()) {
Ok(abi) => {
// try to derive the signature from the abi from the parsed abi
// TODO(mattsse): this will fail for events that contain other non elementary types in their abi
// because the parser doesn't know how to substitute the types
// this could be mitigated by getting the ABI of each non elementary type at runtime
// and computing the the signature as `static Lazy::...`
match parse_event(&abi) {
Ok(mut event) => {
event.name = event_name.clone();
(event.abi_signature(), event.signature())
}
Err(err) => {
return TokenStream::from(Error::new(span, err).to_compile_error())
// try as tuple
if let Some(inputs) = Reader::read(
src.trim_start_matches("event ")
.trim_start()
.trim_start_matches(&event_name),
)
.ok()
.and_then(|param| match param {
ParamType::Tuple(params) => Some(
params
.into_iter()
.map(|kind| EventParam {
name: "".to_string(),
indexed: false,
kind,
})
.collect(),
),
_ => None,
}) {
let event = Event {
name: event_name.clone(),
inputs,
anonymous: false,
};
(event.abi_signature(), event.signature())
} else {
match src.parse::<Source>().and_then(|s| s.get()) {
Ok(abi) => {
// try to derive the signature from the abi from the parsed abi
// TODO(mattsse): this will fail for events that contain other non elementary types in their abi
// because the parser doesn't know how to substitute the types
// this could be mitigated by getting the ABI of each non elementary type at runtime
// and computing the the signature as `static Lazy::...`
match parse_event(&abi) {
Ok(mut event) => {
event.name = event_name.clone();
(event.abi_signature(), event.signature())
}
Err(err) => {
return TokenStream::from(Error::new(span, err).to_compile_error())
}
}
}
Err(err) => return TokenStream::from(Error::new(span, err).to_compile_error()),
}
Err(err) => return TokenStream::from(Error::new(span, err).to_compile_error()),
}
}
} else {

View File

@ -84,7 +84,7 @@ fn can_detokenize_nested_tuple_struct() {
#[test]
fn can_derive_eth_event() {
#[derive(Debug, Clone, PartialEq, EthEvent)]
pub struct ValueChangedEvent {
struct ValueChangedEvent {
old_author: Address,
new_author: Address,
old_value: String,
@ -112,7 +112,7 @@ fn can_derive_eth_event() {
fn can_set_eth_event_name_attribute() {
#[derive(Debug, PartialEq, EthEvent)]
#[ethevent(name = "MyEvent")]
pub struct ValueChangedEvent {
struct ValueChangedEvent {
old_author: Address,
new_author: Address,
old_value: String,
@ -165,19 +165,40 @@ fn can_detect_various_event_abi_types() {
);
}
// #[test]
// fn can_set_eth_abi_attribute() {
// #[derive(Debug, Clone, PartialEq, EthAbiType)]
// struct SomeType {
// inner: Address,
// msg: String,
// }
//
// #[derive(Debug, PartialEq, EthEvent)]
// #[ethevent(abi = "ValueChangedEvent(address,(address,string),string)")]
// pub struct ValueChangedEvent {
// old_author: Address,
// inner: SomeType,
// new_value: String,
// }
// }
#[test]
fn can_set_eth_abi_attribute() {
#[derive(Debug, Clone, PartialEq, EthAbiType)]
struct SomeType {
inner: Address,
msg: String,
}
#[derive(Debug, PartialEq, EthEvent)]
#[ethevent(abi = "ValueChangedEvent(address,(address,string),string)")]
struct ValueChangedEvent {
old_author: Address,
inner: SomeType,
new_value: String,
}
assert_eq!(
"ValueChangedEvent(address,(address,string),string)",
ValueChangedEvent::abi_signature()
);
#[derive(Debug, PartialEq, EthEvent)]
#[ethevent(
name = "ValueChangedEvent",
abi = "ValueChangedEvent(address,(address,string),string)"
)]
struct ValueChangedEvent2 {
old_author: Address,
inner: SomeType,
new_value: String,
}
assert_eq!(
"ValueChangedEvent(address,(address,string),string)",
ValueChangedEvent2::abi_signature()
);
}