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

feat: Solidity events support #83

Merged
merged 23 commits into from
Jun 12, 2023
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
315 changes: 262 additions & 53 deletions crates/sol-macro/src/expand/mod.rs

Large diffs are not rendered by default.

55 changes: 24 additions & 31 deletions crates/sol-macro/src/expand/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,25 @@ pub fn expand_type(ty: &Type) -> TokenStream {

fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) {
let tts = match *ty {
Type::Address(span, _) => quote_spanned! {span=>
::alloy_sol_types::sol_data::Address
},
Type::Address(span, _) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Address },
Type::Bool(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Bool },
Type::String(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::String },
Type::Bytes(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Bytes },

Type::Bytes { span, size: None } => {
quote_spanned! {span=> ::alloy_sol_types::sol_data::Bytes }
}
Type::Bytes {
span,
size: Some(size),
} => {
Type::FixedBytes(span, size) => {
let size = Literal::u16_unsuffixed(size.get());
quote_spanned! {span=>
::alloy_sol_types::sol_data::FixedBytes<#size>
}
}

Type::Int { span, size } => {
let size = Literal::u16_unsuffixed(size.map(NonZeroU16::get).unwrap_or(256));
Type::Int(span, size) => {
let size = Literal::u16_unsuffixed(size.map_or(256, NonZeroU16::get));
quote_spanned! {span=>
::alloy_sol_types::sol_data::Int<#size>
}
}
Type::Uint { span, size } => {
let size = Literal::u16_unsuffixed(size.map(NonZeroU16::get).unwrap_or(256));
Type::Uint(span, size) => {
let size = Literal::u16_unsuffixed(size.map_or(256, NonZeroU16::get));
quote_spanned! {span=>
::alloy_sol_types::sol_data::Uint<#size>
}
Expand Down Expand Up @@ -82,7 +74,7 @@ impl ExpCtxt<'_> {
}
}

fn custom_type(&self, name: &SolPath) -> &Type {
pub(super) fn custom_type(&self, name: &SolPath) -> &Type {
match self.custom_types.get(name.last_tmp()) {
Some(item) => item,
None => panic!("unresolved item: {name}"),
Expand All @@ -106,14 +98,12 @@ impl ExpCtxt<'_> {
// static types: 1 word
Type::Address(..)
| Type::Bool(_)
| Type::Int { .. }
| Type::Uint { .. }
| Type::Bytes { size: Some(_), .. } => 32,
| Type::Int(..)
| Type::Uint(..)
| Type::FixedBytes(..) => 32,

// dynamic types: 1 offset word, 1 length word
Type::String(_)
| Type::Bytes { size: None, .. }
| Type::Array(SolArray { size: None, .. }) => 64,
Type::String(_) | Type::Bytes(_) | Type::Array(SolArray { size: None, .. }) => 64,

// fixed array: size * encoded size
Type::Array(SolArray {
Expand All @@ -136,7 +126,9 @@ impl ExpCtxt<'_> {
.map(|ty| self.type_base_data_size(ty))
.sum(),
Item::Udt(udt) => self.type_base_data_size(&udt.ty),
Item::Contract(_) | Item::Error(_) | Item::Function(_) => unreachable!(),
Item::Contract(_) | Item::Error(_) | Item::Event(_) | Item::Function(_) => {
unreachable!()
}
},
}
}
Expand All @@ -149,12 +141,11 @@ impl ExpCtxt<'_> {
| Type::Bool(_)
| Type::Int { .. }
| Type::Uint { .. }
| Type::Bytes { size: Some(_), .. } => self.type_base_data_size(ty).into_token_stream(),
| Type::FixedBytes(..) => quote!(32usize),

// dynamic types: 1 offset word, 1 length word, length rounded up to word size
Type::String(_) | Type::Bytes { size: None, .. } => {
let base = self.type_base_data_size(ty);
quote!(#base + (#field.len() / 31) * 32)
Type::String(_) | Type::Bytes(..) => {
quote! { (64usize + ::alloy_sol_types::next_multiple_of_32(#field.len())) }
}
Type::Array(SolArray {
ty: inner,
Expand All @@ -163,7 +154,7 @@ impl ExpCtxt<'_> {
}) => {
let base = self.type_base_data_size(ty);
let inner_size = self.type_data_size(inner, field.clone());
quote!(#base + #field.len() * (#inner_size))
quote! { (#base + #field.len() * (#inner_size)) }
}

// fixed array: size * encoded size
Expand All @@ -175,7 +166,7 @@ impl ExpCtxt<'_> {
let base = self.type_base_data_size(ty);
let inner_size = self.type_data_size(inner, field);
let len: usize = size.base10_parse().unwrap();
quote!(#base + #len * (#inner_size))
quote! { (#base + #len * (#inner_size)) }
}

// tuple: sum of encoded sizes
Expand All @@ -185,13 +176,15 @@ impl ExpCtxt<'_> {
let field_name = quote!(#field.#index);
self.type_data_size(ty, field_name)
});
quote!(0usize #(+ #fields)*)
quote! { (0usize #(+ #fields)*) }
}

Type::Custom(name) => match self.get_item(name) {
Item::Struct(strukt) => self.params_data_size(&strukt.fields, Some(field)),
Item::Udt(udt) => self.type_data_size(&udt.ty, field),
Item::Contract(_) | Item::Error(_) | Item::Function(_) => unreachable!(),
Item::Contract(_) | Item::Error(_) | Item::Event(_) | Item::Function(_) => {
unreachable!()
}
},
}
}
Expand Down
7 changes: 6 additions & 1 deletion crates/sol-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@ mod utils;
#[doc = include_str!("../../sol-types/tests/doc_types.rs")]
/// ```
///
/// ## Functions, errors, and events
/// ## Functions and errors
/// ```ignore
#[doc = include_str!("../../sol-types/tests/doc_function_like.rs")]
/// ```
///
/// ## Events
/// ```ignore
#[doc = include_str!("../../sol-types/tests/doc_events.rs")]
/// ```
///
/// ## Contracts/interfaces
/// ```ignore
#[doc = include_str!("../../sol-types/tests/doc_contracts.rs")]
Expand Down
39 changes: 33 additions & 6 deletions crates/sol-macro/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use proc_macro2::TokenStream;
use quote::quote;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use tiny_keccak::{Hasher, Keccak};

/// Simple interface to the [`keccak256`] hash function.
Expand All @@ -13,10 +13,12 @@ pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> [u8; 32] {
output
}

pub fn selector<T: AsRef<[u8]>>(bytes: T) -> TokenStream {
let hash = keccak256(bytes);
let selector: [u8; 4] = hash[..4].try_into().unwrap();
quote!([#(#selector),*])
pub fn selector<T: AsRef<[u8]>>(bytes: T) -> impl ToTokens {
ExprArray::<_, 4>::new(keccak256(bytes)[..4].try_into().unwrap())
}

pub fn event_selector<T: AsRef<[u8]>>(bytes: T) -> impl ToTokens {
ExprArray::new(keccak256(bytes))
}

pub fn combine_errors(v: Vec<syn::Error>) -> Option<syn::Error> {
Expand All @@ -25,3 +27,28 @@ pub fn combine_errors(v: Vec<syn::Error>) -> Option<syn::Error> {
a
})
}

struct ExprArray<T, const N: usize> {
array: [T; N],
span: Span,
}

impl<T, const N: usize> ExprArray<T, N> {
fn new(array: [T; N]) -> Self {
Self {
array,
span: Span::call_site(),
}
}
}

impl<T: ToTokens, const N: usize> ToTokens for ExprArray<T, N> {
fn to_tokens(&self, tokens: &mut TokenStream) {
syn::token::Bracket(self.span).surround(tokens, |tokens| {
for t in &self.array {
t.to_tokens(tokens);
syn::token::Comma(self.span).to_tokens(tokens);
}
})
}
}
2 changes: 1 addition & 1 deletion crates/sol-types/src/coder/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl<'a> Decoder<'a> {
/// word boundary.
pub fn take_slice(&mut self, len: usize) -> Result<&[u8], Error> {
if self.validate {
let padded_len = util::round_up_nearest_multiple(len, 32);
let padded_len = util::next_multiple_of_32(len);
if self.offset + padded_len > self.buf.len() {
return Err(Error::Overrun)
}
Expand Down
20 changes: 8 additions & 12 deletions crates/sol-types/src/coder/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,9 +473,7 @@ impl PackedSeqToken {
}

macro_rules! tuple_impls {
() => {};
(@peel $_:ident, $($other:ident,)*) => { tuple_impls! { $($other,)* } };
($($ty:ident,)+) => {
($($ty:ident),+) => {
impl<$($ty: TokenType,)+> Sealed for ($($ty,)+) {}

#[allow(non_snake_case)]
Expand Down Expand Up @@ -510,7 +508,7 @@ macro_rules! tuple_impls {
if Self::is_dynamic() {
1
} else {
let ($(ref $ty,)+) = *self;
let ($($ty,)+) = self;
0 $( + $ty.head_words() )+
}
}
Expand All @@ -526,15 +524,15 @@ macro_rules! tuple_impls {

#[inline]
fn total_words(&self) -> usize {
let ($(ref $ty,)+) = *self;
let ($($ty,)+) = self;
0 $( + $ty.total_words() )+
}

fn head_append(&self, enc: &mut Encoder) {
if Self::is_dynamic() {
enc.append_indirection();
} else {
let ($(ref $ty,)+) = *self;
let ($($ty,)+) = self;
$(
$ty.head_append(enc);
)+
Expand All @@ -543,7 +541,7 @@ macro_rules! tuple_impls {

fn tail_append(&self, enc: &mut Encoder) {
if Self::is_dynamic() {
let ($(ref $ty,)+) = *self;
let ($($ty,)+) = self;
let head_words = 0 $( + $ty.head_words() )+;

enc.push_offset(head_words as u32);
Expand All @@ -564,7 +562,7 @@ macro_rules! tuple_impls {
const IS_TUPLE: bool = true;

fn encode_sequence(&self, enc: &mut Encoder) {
let ($(ref $ty,)+) = *self;
let ($($ty,)+) = self;
let head_words = 0 $( + $ty.head_words() )+;
enc.push_offset(head_words as u32);
$(
Expand All @@ -583,13 +581,9 @@ macro_rules! tuple_impls {
)+))
}
}

tuple_impls! { @peel $($ty,)+ }
};
}

tuple_impls! { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, }

impl TokenType for () {
#[inline]
fn is_dynamic() -> bool {
Expand Down Expand Up @@ -630,6 +624,8 @@ impl TokenSeq for () {
}
}

all_the_tuples!(tuple_impls);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 6 additions & 2 deletions crates/sol-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ pub mod no_std_prelude {
};
}

#[macro_use]
mod macros;

mod coder;
pub use coder::{
decode, decode_params, decode_single, encode, encode_params, encode_single,
Expand All @@ -180,12 +183,13 @@ pub use errors::{Error, Result};

mod types;
pub use types::{
data_type as sol_data, Panic, PanicKind, Revert, SolCall, SolError, SolStruct, SolType,
data_type as sol_data, EventTopic, Panic, PanicKind, Revert, SolCall, SolError, SolEvent,
SolStruct, SolType, TopicList,
};

mod util;
#[doc(hidden)]
pub use util::just_ok;
pub use util::{just_ok, next_multiple_of_32};

mod eip712;
pub use eip712::Eip712Domain;
Expand Down
30 changes: 30 additions & 0 deletions crates/sol-types/src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/// Calls the given macro with all the tuples.
#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
$name!(T1);
$name!(T1, T2);
$name!(T1, T2, T3);
$name!(T1, T2, T3, T4);
$name!(T1, T2, T3, T4, T5);
$name!(T1, T2, T3, T4, T5, T6);
$name!(T1, T2, T3, T4, T5, T6, T7);
$name!(T1, T2, T3, T4, T5, T6, T7, T8);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23);
$name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24);
};
}
Loading