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

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

Merged
merged 3 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 67 additions & 18 deletions ethers-contract/ethers-contract-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand Down
57 changes: 39 additions & 18 deletions ethers-contract/tests/common/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
);
}