Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sol-macro): correctly determine whether event parameters are hashes #735

Merged
merged 1 commit into from
Sep 10, 2024
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
6 changes: 3 additions & 3 deletions crates/sol-macro-expander/src/expand/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result<TokenStream>
let name = anon_name((i, p.name.as_ref()));
let ty = expand_type(&p.ty, &cx.crates);

if p.indexed_as_hash() {
if cx.indexed_as_hash(p) {
quote! {
<alloy_sol_types::sol_data::FixedBytes<32> as alloy_sol_types::EventTopic>::encode_topic(&self.#name)
}
Expand Down Expand Up @@ -225,7 +225,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result<TokenStream>
fn expand_event_topic_type(param: &EventParameter, cx: &ExpCtxt<'_>) -> TokenStream {
let alloy_sol_types = &cx.crates.sol_types;
assert!(param.is_indexed());
if param.is_abi_dynamic() {
if cx.indexed_as_hash(param) {
quote_spanned! {param.ty.span()=> #alloy_sol_types::sol_data::FixedBytes<32> }
} else {
expand_type(&param.ty, &cx.crates)
Expand All @@ -239,7 +239,7 @@ fn expand_event_topic_field(
cx: &ExpCtxt<'_>,
) -> TokenStream {
let name = anon_name((i, name));
let ty = if param.indexed_as_hash() {
let ty = if cx.indexed_as_hash(param) {
let bytes32 = ast::Type::FixedBytes(name.span(), core::num::NonZeroU16::new(32).unwrap());
ty::expand_rust_type(&bytes32, &cx.crates)
} else {
Expand Down
18 changes: 17 additions & 1 deletion crates/sol-macro-expander/src/expand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,23 @@ impl<'ast> ExpCtxt<'ast> {
}

fn try_custom_type(&self, name: &SolPath) -> Option<&Type> {
self.custom_types.resolve(name, &self.current_namespace)
self.custom_types.resolve(name, &self.current_namespace).inspect(|&ty| {
if ty.is_custom() {
abort!(
ty.span(),
"unresolved custom type in map";
note = name.span() => "name span";
);
}
})
}

fn indexed_as_hash(&self, param: &EventParameter) -> bool {
param.indexed_as_hash(self.custom_is_value_type())
}

fn custom_is_value_type(&self) -> impl Fn(&SolPath) -> bool + '_ {
move |ty| self.custom_type(ty).is_value_type(self.custom_is_value_type())
}

/// Returns the name of the function, adjusted for overloads.
Expand Down
1 change: 1 addition & 0 deletions crates/sol-types/src/types/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub trait SolEvent: Sized {
///
/// For non-anonymous events, this will be the first topic (`topic0`).
/// For anonymous events, this is unused, but is still present.
#[doc(alias = "SELECTOR")]
const SIGNATURE_HASH: FixedBytes<32>;

/// Whether the event is anonymous.
Expand Down
64 changes: 64 additions & 0 deletions crates/sol-types/tests/macros/sol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,3 +1084,67 @@ fn regression_nested_namespaced_structs() {
assert_eq!(inner::C::libCNested1Call::SIGNATURE, format!("libCNested1({c_nested1})"));
assert_eq!(inner::C::libCNested2Call::SIGNATURE, format!("libCNested2({c_nested2})"));
}

// https://github.com/alloy-rs/core/issues/734
#[test]
fn event_indexed_udvt() {
use alloy_primitives::aliases::*;

sol! {
type Currency is address;
type PoolId is bytes32;

event Initialize(
PoolId indexed id,
Currency indexed currency0,
Currency indexed currency1,
uint24 fee,
int24 tickSpacing,
address hooks,
uint160 sqrtPriceX96,
int24 tick
);
}

assert_eq!(
Initialize::SIGNATURE,
"Initialize(bytes32,address,address,uint24,int24,address,uint160,int24)",
);
assert_eq!(
Initialize::SIGNATURE_HASH,
b256!("dd466e674ea557f56295e2d0218a125ea4b4f0f6f3307b95f85e6110838d6438"),
);

let _ = Initialize {
id: B256::ZERO,
currency0: Address::ZERO,
currency1: Address::ZERO,
fee: U24::ZERO,
tickSpacing: I24::ZERO,
hooks: Address::ZERO,
sqrtPriceX96: U160::ZERO,
tick: I24::ZERO,
};
}

#[test]
fn event_indexed_elementary_arrays() {
sol! {
event AddrArray(address[1] indexed x);
event AddrDynArray(address[] indexed x);

type MyAddress is address;
event AddrUdvtArray(MyAddress[1] indexed y);
event AddrUdvtDynArray(MyAddress[] indexed y);
}

assert_eq!(AddrArray::SIGNATURE, "AddrArray(address[1])");
let _ = AddrArray { x: B256::ZERO };
assert_eq!(AddrDynArray::SIGNATURE, "AddrDynArray(address[])");
let _ = AddrDynArray { x: B256::ZERO };

assert_eq!(AddrUdvtArray::SIGNATURE, "AddrUdvtArray(address[1])");
let _ = AddrUdvtArray { y: B256::ZERO };
assert_eq!(AddrUdvtDynArray::SIGNATURE, "AddrUdvtDynArray(address[])");
let _ = AddrUdvtDynArray { y: B256::ZERO };
}
18 changes: 6 additions & 12 deletions crates/syn-solidity/src/item/event.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
kw, utils::DebugPunctuated, ParameterList, SolIdent, Spanned, Type, VariableDeclaration,
kw, utils::DebugPunctuated, ParameterList, SolIdent, SolPath, Spanned, Type,
VariableDeclaration,
};
use proc_macro2::Span;
use std::fmt;
Expand Down Expand Up @@ -153,10 +154,6 @@ impl ItemEvent {
self.parameters.iter().filter(|p| p.is_indexed())
}

pub fn dynamic_params(&self) -> impl Iterator<Item = &EventParameter> {
self.parameters.iter().filter(|p| p.is_abi_dynamic())
}

pub fn as_type(&self) -> Type {
let mut ty = Type::Tuple(self.parameters.iter().map(|arg| arg.ty.clone()).collect());
ty.set_span(self.span());
Expand Down Expand Up @@ -252,17 +249,14 @@ impl EventParameter {
self.indexed.is_none()
}

/// Returns true if the event parameter is a dynamically sized type.
pub fn is_abi_dynamic(&self) -> bool {
self.ty.is_abi_dynamic()
}

/// Returns `true` if the event parameter is indexed and dynamically sized.
/// These types are hashed, and then stored in the topics as specified in
/// [the Solidity spec][ref].
///
/// `custom_is_value_type` accounts for custom value types.
///
/// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#events
pub fn indexed_as_hash(&self) -> bool {
self.is_indexed() && self.is_abi_dynamic()
pub fn indexed_as_hash(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
self.is_indexed() && !self.ty.is_value_type(custom_is_value_type)
}
}
46 changes: 31 additions & 15 deletions crates/syn-solidity/src/type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,21 +278,17 @@ impl Type {
Ok(self)
}

/// Returns whether this type is ABI-encoded as a single EVM word (32
/// bytes).
pub const fn is_one_word(&self) -> bool {
matches!(
self,
Self::Bool(_)
| Self::Int(..)
| Self::Uint(..)
| Self::FixedBytes(..)
| Self::Address(..)
| Self::Function(_)
)
/// Returns whether this type is ABI-encoded as a single EVM word (32 bytes).
///
/// This is the same as [`is_value_type`](Self::is_value_type).
#[deprecated = "use `is_value_type` instead"]
pub fn is_one_word(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
self.is_value_type(custom_is_value_type)
}

/// Returns whether this type is dynamic according to ABI rules.
///
/// Note that this does not account for custom types, such as UDVTs.
pub fn is_abi_dynamic(&self) -> bool {
match self {
Self::Bool(_)
Expand All @@ -308,17 +304,37 @@ impl Type {
Self::Tuple(tuple) => tuple.is_abi_dynamic(),

// not applicable
Self::Mapping(_) => false,
Self::Mapping(_) => true,
}
}

/// Returns whether this type is a value type.
///
/// These types' variables are always passed by value.
///
/// `custom_is_value_type` accounts for custom value types.
///
/// See the [Solidity docs](https://docs.soliditylang.org/en/latest/types.html#value-types) for more information.
pub const fn is_value_type(&self) -> bool {
self.is_one_word()
pub fn is_value_type(&self, custom_is_value_type: impl Fn(&SolPath) -> bool) -> bool {
match self {
Self::Custom(custom) => custom_is_value_type(custom),
_ => self.is_value_type_simple(),
}
}

/// Returns whether this type is a simple value type.
///
/// See [`is_value_type`](Self::is_value_type) for more information.
pub fn is_value_type_simple(&self) -> bool {
matches!(
self,
Self::Bool(_)
| Self::Int(..)
| Self::Uint(..)
| Self::FixedBytes(..)
| Self::Address(..)
| Self::Function(_)
)
}

pub const fn is_array(&self) -> bool {
Expand Down