alloy_sol_types/types/event/
topic.rs

1use crate::{abi::token::WordToken, sol_data::*, SolType};
2use alloc::vec::Vec;
3use alloy_primitives::keccak256;
4
5/// A Solidity event topic.
6///
7/// These types implement a special encoding used only in Solidity indexed event
8/// parameters.
9///
10/// For more details, see the [Solidity reference][ref].
11///
12/// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#encoding-of-indexed-event-parameters
13///
14/// # Implementer's Guide
15///
16/// It should not be necessary to implement this trait manually. Instead, use
17/// the [`sol!`](crate::sol!) procedural macro to parse Solidity syntax into
18/// types that implement this trait.
19pub trait EventTopic: SolType {
20    /// The number of bytes this type occupies in another topic's preimage,
21    /// usually a multiple of 32.
22    ///
23    /// This should be used in conjunction with [`encode_topic_preimage`] to
24    /// construct the preimage of a complex topic.
25    ///
26    /// [`encode_topic_preimage`]: EventTopic::encode_topic_preimage
27    fn topic_preimage_length(rust: &Self::RustType) -> usize;
28
29    /// Encodes this type as preimage bytes which are then hashed in
30    /// complex types' [`encode_topic`][EventTopic::encode_topic].
31    ///
32    /// See the [Solidity ABI spec][ref] for more details.
33    ///
34    /// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#encoding-of-indexed-event-parameters
35    fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec<u8>);
36
37    /// Indexed event parameter encoding.
38    ///
39    /// Note that this is different from [`encode_topic_preimage`] and
40    /// [`SolType::abi_encode`]. See the [Solidity ABI spec][ref] for more
41    /// details.
42    ///
43    /// [`encode_topic_preimage`]: EventTopic::encode_topic_preimage
44    /// [ref]: https://docs.soliditylang.org/en/latest/abi-spec.html#encoding-of-indexed-event-parameters
45    fn encode_topic(rust: &Self::RustType) -> WordToken;
46}
47
48// Single word types: encoded as just the single word
49macro_rules! word_impl {
50    () => {
51        #[inline]
52        fn topic_preimage_length(_: &Self::RustType) -> usize {
53            32
54        }
55
56        #[inline]
57        fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec<u8>) {
58            out.extend($crate::private::SolTypeValue::<Self>::stv_to_tokens(rust).0);
59        }
60
61        #[inline]
62        fn encode_topic(rust: &Self::RustType) -> WordToken {
63            $crate::private::SolTypeValue::<Self>::stv_to_tokens(rust)
64        }
65    };
66}
67
68impl EventTopic for Address {
69    word_impl!();
70}
71
72impl EventTopic for Function {
73    word_impl!();
74}
75
76impl EventTopic for Bool {
77    word_impl!();
78}
79
80impl<const BITS: usize> EventTopic for Int<BITS>
81where
82    IntBitCount<BITS>: SupportedInt,
83{
84    word_impl!();
85}
86
87impl<const BITS: usize> EventTopic for Uint<BITS>
88where
89    IntBitCount<BITS>: SupportedInt,
90{
91    word_impl!();
92}
93
94impl<const N: usize> EventTopic for FixedBytes<N>
95where
96    ByteCount<N>: SupportedFixedBytes,
97{
98    word_impl!();
99}
100
101// Bytes-like types - preimage encoding: bytes padded to 32; hash: the bytes
102macro_rules! bytes_impl {
103    () => {
104        #[inline]
105        fn topic_preimage_length(rust: &Self::RustType) -> usize {
106            crate::utils::next_multiple_of_32(rust.len())
107        }
108
109        #[inline]
110        fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec<u8>) {
111            encode_topic_bytes(rust.as_ref(), out);
112        }
113
114        #[inline]
115        fn encode_topic(rust: &Self::RustType) -> WordToken {
116            WordToken(keccak256(rust))
117        }
118    };
119}
120
121impl EventTopic for String {
122    bytes_impl!();
123}
124
125impl EventTopic for Bytes {
126    bytes_impl!();
127}
128
129// Complex types - preimage encoding and hash: iter each element
130macro_rules! array_impl {
131    ($ty:ident) => {
132        #[inline]
133        fn topic_preimage_length(rust: &Self::RustType) -> usize {
134            rust.iter().map($ty::topic_preimage_length).sum()
135        }
136
137        #[inline]
138        fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec<u8>) {
139            out.reserve(Self::topic_preimage_length(rust));
140            for t in rust {
141                $ty::encode_topic_preimage(t, out);
142            }
143        }
144
145        #[inline]
146        fn encode_topic(rust: &Self::RustType) -> WordToken {
147            let mut out = Vec::new();
148            Self::encode_topic_preimage(rust, &mut out);
149            WordToken(keccak256(out))
150        }
151    };
152}
153
154impl<T: EventTopic> EventTopic for Array<T> {
155    array_impl!(T);
156}
157
158impl<T: EventTopic, const N: usize> EventTopic for FixedArray<T, N> {
159    array_impl!(T);
160}
161
162macro_rules! tuple_impls {
163    ($count:literal $($ty:ident),+) => {
164        #[allow(non_snake_case)]
165        impl<$($ty: EventTopic,)+> EventTopic for ($($ty,)+) {
166            #[inline]
167            fn topic_preimage_length(rust: &Self::RustType) -> usize {
168                let ($($ty,)+) = rust;
169                0usize $( + <$ty>::topic_preimage_length($ty) )+
170            }
171
172            #[inline]
173            fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec<u8>) {
174                let b @ ($($ty,)+) = rust;
175                out.reserve(Self::topic_preimage_length(b));
176                $(
177                    <$ty>::encode_topic_preimage($ty, out);
178                )+
179            }
180
181            #[inline]
182            fn encode_topic(rust: &Self::RustType) -> WordToken {
183                let mut out = Vec::new();
184                Self::encode_topic_preimage(rust, &mut out);
185                WordToken(keccak256(out))
186            }
187        }
188    };
189}
190
191impl EventTopic for () {
192    #[inline]
193    fn topic_preimage_length(_: &Self::RustType) -> usize {
194        0
195    }
196
197    #[inline]
198    fn encode_topic_preimage(_: &Self::RustType, _: &mut Vec<u8>) {}
199
200    #[inline]
201    fn encode_topic(_: &Self::RustType) -> WordToken {
202        WordToken::default()
203    }
204}
205
206all_the_tuples!(tuple_impls);
207
208fn encode_topic_bytes(sl: &[u8], out: &mut Vec<u8>) {
209    let padding = 32 - sl.len() % 32;
210    out.reserve(sl.len() + padding);
211    out.extend_from_slice(sl);
212    out.extend(core::iter::repeat(0).take(padding));
213}