alloy_sol_types/types/
data_type.rs

1//! Solidity types.
2//!
3//! These are the types that are [built into Solidity][ref].
4//!
5//! See [`SolType`] for more details.
6//!
7//! [ref]: https://docs.soliditylang.org/en/latest/types.html
8
9#![allow(missing_copy_implementations, missing_debug_implementations)]
10
11use crate::{SolType, Word, abi::token::*, private::SolTypeValue, utils};
12use alloc::{string::String as RustString, vec::Vec};
13use alloy_primitives::{
14    Address as RustAddress, Bytes as RustBytes, FixedBytes as RustFixedBytes,
15    Function as RustFunction, I256, U256, aliases::*, keccak256,
16};
17use core::{borrow::Borrow, fmt::*, hash::Hash, marker::PhantomData, ops::*};
18
19// IMPORTANT: Keep in sync with `rec_expand_rust_type` in
20// `crates/sol-macro-expander/src/expand/ty.rs`
21
22/// Bool - `bool`
23pub struct Bool;
24
25impl SolTypeValue<Bool> for bool {
26    #[inline]
27    fn stv_to_tokens(&self) -> WordToken {
28        WordToken(Word::with_last_byte(*self as u8))
29    }
30
31    #[inline]
32    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
33        out.push(*self as u8);
34    }
35
36    #[inline]
37    fn stv_eip712_data_word(&self) -> Word {
38        SolTypeValue::<Bool>::stv_to_tokens(self).0
39    }
40}
41
42impl SolType for Bool {
43    type RustType = bool;
44    type Token<'a> = WordToken;
45
46    const SOL_NAME: &'static str = "bool";
47    const ENCODED_SIZE: Option<usize> = Some(32);
48    const PACKED_ENCODED_SIZE: Option<usize> = Some(1);
49
50    #[inline]
51    fn valid_token(token: &Self::Token<'_>) -> bool {
52        utils::check_zeroes(&token.0[..31])
53    }
54
55    #[inline]
56    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
57        token.0 != Word::ZERO
58    }
59}
60
61/// Int - `intX`
62pub struct Int<const BITS: usize>;
63
64impl<T, const BITS: usize> SolTypeValue<Int<BITS>> for T
65where
66    T: Borrow<<IntBitCount<BITS> as SupportedInt>::Int>,
67    IntBitCount<BITS>: SupportedInt,
68{
69    #[inline]
70    fn stv_to_tokens(&self) -> WordToken {
71        IntBitCount::<BITS>::tokenize_int(*self.borrow())
72    }
73
74    #[inline]
75    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
76        IntBitCount::<BITS>::encode_packed_to_int(*self.borrow(), out);
77    }
78
79    #[inline]
80    fn stv_eip712_data_word(&self) -> Word {
81        SolTypeValue::<Int<BITS>>::stv_to_tokens(self).0
82    }
83}
84
85impl<const BITS: usize> SolType for Int<BITS>
86where
87    IntBitCount<BITS>: SupportedInt,
88{
89    type RustType = <IntBitCount<BITS> as SupportedInt>::Int;
90    type Token<'a> = WordToken;
91
92    const SOL_NAME: &'static str = IntBitCount::<BITS>::INT_NAME;
93    const ENCODED_SIZE: Option<usize> = Some(32);
94    const PACKED_ENCODED_SIZE: Option<usize> = Some(BITS / 8);
95
96    #[inline]
97    fn valid_token(token: &Self::Token<'_>) -> bool {
98        if BITS == 256 {
99            return true;
100        }
101
102        let is_negative = token.0[IntBitCount::<BITS>::WORD_MSB] & 0x80 == 0x80;
103        let sign_extension = is_negative as u8 * 0xff;
104
105        // check that all upper bytes are an extension of the sign bit
106        token.0[..IntBitCount::<BITS>::WORD_MSB].iter().all(|byte| *byte == sign_extension)
107    }
108
109    #[inline]
110    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
111        IntBitCount::<BITS>::detokenize_int(token)
112    }
113}
114
115/// Uint - `uintX`
116pub struct Uint<const BITS: usize>;
117
118impl<const BITS: usize, T> SolTypeValue<Uint<BITS>> for T
119where
120    T: Borrow<<IntBitCount<BITS> as SupportedInt>::Uint>,
121    IntBitCount<BITS>: SupportedInt,
122{
123    #[inline]
124    fn stv_to_tokens(&self) -> WordToken {
125        IntBitCount::<BITS>::tokenize_uint(*self.borrow())
126    }
127
128    #[inline]
129    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
130        IntBitCount::<BITS>::encode_packed_to_uint(*self.borrow(), out);
131    }
132
133    #[inline]
134    fn stv_eip712_data_word(&self) -> Word {
135        SolTypeValue::<Uint<BITS>>::stv_to_tokens(self).0
136    }
137}
138
139impl<const BITS: usize> SolType for Uint<BITS>
140where
141    IntBitCount<BITS>: SupportedInt,
142{
143    type RustType = <IntBitCount<BITS> as SupportedInt>::Uint;
144    type Token<'a> = WordToken;
145
146    const SOL_NAME: &'static str = IntBitCount::<BITS>::UINT_NAME;
147    const ENCODED_SIZE: Option<usize> = Some(32);
148    const PACKED_ENCODED_SIZE: Option<usize> = Some(BITS / 8);
149
150    #[inline]
151    fn valid_token(token: &Self::Token<'_>) -> bool {
152        utils::check_zeroes(&token.0[..<IntBitCount<BITS> as SupportedInt>::WORD_MSB])
153    }
154
155    #[inline]
156    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
157        IntBitCount::<BITS>::detokenize_uint(token)
158    }
159}
160
161/// FixedBytes - `bytesX`
162#[derive(Clone, Copy, Debug)]
163pub struct FixedBytes<const N: usize>;
164
165impl<T: Borrow<[u8; N]>, const N: usize> SolTypeValue<FixedBytes<N>> for T
166where
167    ByteCount<N>: SupportedFixedBytes,
168{
169    #[inline]
170    fn stv_to_tokens(&self) -> <FixedBytes<N> as SolType>::Token<'_> {
171        let mut word = Word::ZERO;
172        word[..N].copy_from_slice(self.borrow());
173        word.into()
174    }
175
176    #[inline]
177    fn stv_eip712_data_word(&self) -> Word {
178        SolTypeValue::<FixedBytes<N>>::stv_to_tokens(self).0
179    }
180
181    #[inline]
182    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
183        out.extend_from_slice(self.borrow().as_slice());
184    }
185}
186
187impl<const N: usize> SolType for FixedBytes<N>
188where
189    ByteCount<N>: SupportedFixedBytes,
190{
191    type RustType = RustFixedBytes<N>;
192    type Token<'a> = WordToken;
193
194    const SOL_NAME: &'static str = <ByteCount<N>>::NAME;
195    const ENCODED_SIZE: Option<usize> = Some(32);
196    const PACKED_ENCODED_SIZE: Option<usize> = Some(N);
197
198    #[inline]
199    fn valid_token(token: &Self::Token<'_>) -> bool {
200        utils::check_zeroes(&token.0[N..])
201    }
202
203    #[inline]
204    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
205        token.0[..N].try_into().unwrap()
206    }
207}
208
209/// Address - `address`
210pub struct Address;
211
212impl<T: Borrow<[u8; 20]>> SolTypeValue<Address> for T {
213    #[inline]
214    fn stv_to_tokens(&self) -> WordToken {
215        WordToken(RustAddress::new(*self.borrow()).into_word())
216    }
217
218    #[inline]
219    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
220        out.extend_from_slice(self.borrow());
221    }
222
223    #[inline]
224    fn stv_eip712_data_word(&self) -> Word {
225        SolTypeValue::<Address>::stv_to_tokens(self).0
226    }
227}
228
229impl SolType for Address {
230    type RustType = RustAddress;
231    type Token<'a> = WordToken;
232
233    const SOL_NAME: &'static str = "address";
234    const ENCODED_SIZE: Option<usize> = Some(32);
235    const PACKED_ENCODED_SIZE: Option<usize> = Some(20);
236
237    #[inline]
238    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
239        RustAddress::from_word(token.0)
240    }
241
242    #[inline]
243    fn valid_token(token: &Self::Token<'_>) -> bool {
244        utils::check_zeroes(&token.0[..12])
245    }
246}
247
248/// Function - `function`
249pub struct Function;
250
251impl<T: Borrow<[u8; 24]>> SolTypeValue<Function> for T {
252    #[inline]
253    fn stv_to_tokens(&self) -> WordToken {
254        WordToken(RustFunction::new(*self.borrow()).into_word())
255    }
256
257    #[inline]
258    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
259        out.extend_from_slice(self.borrow());
260    }
261
262    #[inline]
263    fn stv_eip712_data_word(&self) -> Word {
264        SolTypeValue::<Function>::stv_to_tokens(self).0
265    }
266}
267
268impl SolType for Function {
269    type RustType = RustFunction;
270    type Token<'a> = WordToken;
271
272    const SOL_NAME: &'static str = "function";
273    const ENCODED_SIZE: Option<usize> = Some(32);
274    const PACKED_ENCODED_SIZE: Option<usize> = Some(24);
275
276    #[inline]
277    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
278        RustFunction::from_word(token.0)
279    }
280
281    #[inline]
282    fn valid_token(token: &Self::Token<'_>) -> bool {
283        utils::check_zeroes(&token.0[24..])
284    }
285}
286
287/// Bytes - `bytes`
288pub struct Bytes;
289
290impl<T: ?Sized + AsRef<[u8]>> SolTypeValue<Bytes> for T {
291    #[inline]
292    fn stv_to_tokens(&self) -> PackedSeqToken<'_> {
293        PackedSeqToken(self.as_ref())
294    }
295
296    #[inline]
297    fn stv_abi_encoded_size(&self) -> usize {
298        let s = self.as_ref();
299        if s.is_empty() { 64 } else { 64 + utils::padded_len(s) }
300    }
301
302    #[inline]
303    fn stv_eip712_data_word(&self) -> Word {
304        keccak256(Bytes::abi_encode_packed(self))
305    }
306
307    #[inline]
308    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
309        out.extend_from_slice(self.as_ref());
310    }
311
312    #[inline]
313    fn stv_abi_packed_encoded_size(&self) -> usize {
314        self.as_ref().len()
315    }
316}
317
318impl SolType for Bytes {
319    type RustType = RustBytes;
320    type Token<'a> = PackedSeqToken<'a>;
321
322    const SOL_NAME: &'static str = "bytes";
323    const ENCODED_SIZE: Option<usize> = None;
324    const PACKED_ENCODED_SIZE: Option<usize> = None;
325
326    #[inline]
327    fn valid_token(_token: &Self::Token<'_>) -> bool {
328        true
329    }
330
331    #[inline]
332    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
333        token.into_bytes()
334    }
335}
336
337/// String - `string`
338pub struct String;
339
340impl<T: ?Sized + AsRef<str>> SolTypeValue<String> for T {
341    #[inline]
342    fn stv_to_tokens(&self) -> PackedSeqToken<'_> {
343        PackedSeqToken(self.as_ref().as_bytes())
344    }
345
346    #[inline]
347    fn stv_abi_encoded_size(&self) -> usize {
348        let s = self.as_ref();
349        if s.is_empty() { 64 } else { 64 + utils::padded_len(s.as_bytes()) }
350    }
351
352    #[inline]
353    fn stv_eip712_data_word(&self) -> Word {
354        keccak256(String::abi_encode_packed(self))
355    }
356
357    #[inline]
358    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
359        out.extend_from_slice(self.as_ref().as_ref());
360    }
361
362    #[inline]
363    fn stv_abi_packed_encoded_size(&self) -> usize {
364        self.as_ref().len()
365    }
366}
367
368impl SolType for String {
369    type RustType = RustString;
370    type Token<'a> = PackedSeqToken<'a>;
371
372    const SOL_NAME: &'static str = "string";
373    const ENCODED_SIZE: Option<usize> = None;
374    const PACKED_ENCODED_SIZE: Option<usize> = None;
375
376    #[inline]
377    fn valid_token(token: &Self::Token<'_>) -> bool {
378        core::str::from_utf8(token.as_slice()).is_ok()
379    }
380
381    #[inline]
382    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
383        // NOTE: We're decoding strings using lossy UTF-8 decoding to
384        // prevent invalid strings written into contracts by either users or
385        // Solidity bugs from causing graph-node to fail decoding event
386        // data.
387        RustString::from_utf8_lossy(token.as_slice()).into_owned()
388    }
389}
390
391/// Array - `T[]`
392pub struct Array<T>(PhantomData<T>);
393
394impl<T, U> SolTypeValue<Array<U>> for [T]
395where
396    T: SolTypeValue<U>,
397    U: SolType,
398{
399    #[inline]
400    fn stv_to_tokens(&self) -> DynSeqToken<U::Token<'_>> {
401        DynSeqToken(self.iter().map(T::stv_to_tokens).collect())
402    }
403
404    #[inline]
405    fn stv_abi_encoded_size(&self) -> usize {
406        if let Some(size) = Array::<U>::ENCODED_SIZE {
407            return size;
408        }
409
410        64 + self.iter().map(T::stv_abi_encoded_size).sum::<usize>()
411    }
412
413    #[inline]
414    fn stv_eip712_data_word(&self) -> Word {
415        let mut encoded = Vec::new();
416        for item in self {
417            encoded.extend_from_slice(T::stv_eip712_data_word(item).as_slice());
418        }
419        keccak256(encoded)
420    }
421
422    #[inline]
423    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
424        for item in self {
425            // Array elements are left-padded to 32 bytes.
426            if let Some(padding_needed) = 32usize.checked_sub(item.stv_abi_packed_encoded_size()) {
427                out.extend(core::iter::repeat_n(0, padding_needed));
428            }
429            T::stv_abi_encode_packed_to(item, out);
430        }
431    }
432
433    #[inline]
434    fn stv_abi_packed_encoded_size(&self) -> usize {
435        self.iter().map(|item| item.stv_abi_packed_encoded_size().max(32)).sum()
436    }
437}
438
439impl<T, U> SolTypeValue<Array<U>> for &[T]
440where
441    T: SolTypeValue<U>,
442    U: SolType,
443{
444    #[inline]
445    fn stv_to_tokens(&self) -> DynSeqToken<U::Token<'_>> {
446        (**self).stv_to_tokens()
447    }
448
449    #[inline]
450    fn stv_abi_encoded_size(&self) -> usize {
451        (**self).stv_abi_encoded_size()
452    }
453
454    #[inline]
455    fn stv_eip712_data_word(&self) -> Word {
456        (**self).stv_eip712_data_word()
457    }
458
459    #[inline]
460    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
461        (**self).stv_abi_encode_packed_to(out)
462    }
463
464    #[inline]
465    fn stv_abi_packed_encoded_size(&self) -> usize {
466        (**self).stv_abi_packed_encoded_size()
467    }
468}
469
470impl<T, U> SolTypeValue<Array<U>> for &mut [T]
471where
472    T: SolTypeValue<U>,
473    U: SolType,
474{
475    #[inline]
476    fn stv_to_tokens(&self) -> DynSeqToken<U::Token<'_>> {
477        (**self).stv_to_tokens()
478    }
479
480    #[inline]
481    fn stv_abi_encoded_size(&self) -> usize {
482        (**self).stv_abi_encoded_size()
483    }
484
485    #[inline]
486    fn stv_eip712_data_word(&self) -> Word {
487        (**self).stv_eip712_data_word()
488    }
489
490    #[inline]
491    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
492        (**self).stv_abi_encode_packed_to(out)
493    }
494
495    #[inline]
496    fn stv_abi_packed_encoded_size(&self) -> usize {
497        (**self).stv_abi_packed_encoded_size()
498    }
499}
500
501impl<T, U> SolTypeValue<Array<U>> for Vec<T>
502where
503    T: SolTypeValue<U>,
504    U: SolType,
505{
506    #[inline]
507    fn stv_to_tokens(&self) -> DynSeqToken<U::Token<'_>> {
508        <[T] as SolTypeValue<Array<U>>>::stv_to_tokens(self)
509    }
510
511    #[inline]
512    fn stv_abi_encoded_size(&self) -> usize {
513        (**self).stv_abi_encoded_size()
514    }
515
516    #[inline]
517    fn stv_eip712_data_word(&self) -> Word {
518        (**self).stv_eip712_data_word()
519    }
520
521    #[inline]
522    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
523        (**self).stv_abi_encode_packed_to(out)
524    }
525
526    #[inline]
527    fn stv_abi_packed_encoded_size(&self) -> usize {
528        (**self).stv_abi_packed_encoded_size()
529    }
530}
531
532impl<T: SolType> SolType for Array<T> {
533    type RustType = Vec<T::RustType>;
534    type Token<'a> = DynSeqToken<T::Token<'a>>;
535
536    const SOL_NAME: &'static str =
537        NameBuffer::new().write_str(T::SOL_NAME).write_str("[]").as_str();
538    const ENCODED_SIZE: Option<usize> = None;
539    const PACKED_ENCODED_SIZE: Option<usize> = None;
540
541    #[inline]
542    fn valid_token(token: &Self::Token<'_>) -> bool {
543        token.0.iter().all(T::valid_token)
544    }
545
546    #[inline]
547    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
548        token.0.into_iter().map(T::detokenize).collect()
549    }
550}
551
552/// FixedArray - `T[M]`
553pub struct FixedArray<T, const N: usize>(PhantomData<T>);
554
555impl<T, U, const N: usize> SolTypeValue<FixedArray<U, N>> for [T; N]
556where
557    T: SolTypeValue<U>,
558    U: SolType,
559{
560    #[inline]
561    fn stv_to_tokens(&self) -> <FixedArray<U, N> as SolType>::Token<'_> {
562        FixedSeqToken(core::array::from_fn(|i| self[i].stv_to_tokens()))
563    }
564
565    #[inline]
566    fn stv_abi_encoded_size(&self) -> usize {
567        if let Some(size) = FixedArray::<U, N>::ENCODED_SIZE {
568            return size;
569        }
570
571        let sum = self.iter().map(T::stv_abi_encoded_size).sum::<usize>();
572        if FixedArray::<U, N>::DYNAMIC { 32 + sum } else { sum }
573    }
574
575    #[inline]
576    fn stv_eip712_data_word(&self) -> Word {
577        let encoded = core::array::from_fn::<_, N, _>(|i| self[i].stv_eip712_data_word().0);
578        keccak256(encoded.as_flattened())
579    }
580
581    #[inline]
582    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
583        for item in self {
584            // Array elements are left-padded to 32 bytes.
585            if let Some(padding_needed) = 32usize.checked_sub(item.stv_abi_packed_encoded_size()) {
586                out.extend(core::iter::repeat_n(0, padding_needed));
587            }
588            item.stv_abi_encode_packed_to(out);
589        }
590    }
591
592    #[inline]
593    fn stv_abi_packed_encoded_size(&self) -> usize {
594        self.iter().map(|item| item.stv_abi_packed_encoded_size().max(32)).sum()
595    }
596}
597
598impl<T, U, const N: usize> SolTypeValue<FixedArray<U, N>> for &[T; N]
599where
600    T: SolTypeValue<U>,
601    U: SolType,
602{
603    #[inline]
604    fn stv_to_tokens(&self) -> <FixedArray<U, N> as SolType>::Token<'_> {
605        <[T; N] as SolTypeValue<FixedArray<U, N>>>::stv_to_tokens(&**self)
606    }
607
608    #[inline]
609    fn stv_abi_encoded_size(&self) -> usize {
610        SolTypeValue::<FixedArray<U, N>>::stv_abi_encoded_size(&**self)
611    }
612
613    #[inline]
614    fn stv_eip712_data_word(&self) -> Word {
615        SolTypeValue::<FixedArray<U, N>>::stv_eip712_data_word(&**self)
616    }
617
618    #[inline]
619    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
620        SolTypeValue::<FixedArray<U, N>>::stv_abi_encode_packed_to(&**self, out)
621    }
622
623    #[inline]
624    fn stv_abi_packed_encoded_size(&self) -> usize {
625        SolTypeValue::<FixedArray<U, N>>::stv_abi_packed_encoded_size(&**self)
626    }
627}
628
629impl<T, U, const N: usize> SolTypeValue<FixedArray<U, N>> for &mut [T; N]
630where
631    T: SolTypeValue<U>,
632    U: SolType,
633{
634    #[inline]
635    fn stv_to_tokens(&self) -> <FixedArray<U, N> as SolType>::Token<'_> {
636        <[T; N] as SolTypeValue<FixedArray<U, N>>>::stv_to_tokens(&**self)
637    }
638
639    #[inline]
640    fn stv_abi_encoded_size(&self) -> usize {
641        SolTypeValue::<FixedArray<U, N>>::stv_abi_encoded_size(&**self)
642    }
643
644    #[inline]
645    fn stv_eip712_data_word(&self) -> Word {
646        SolTypeValue::<FixedArray<U, N>>::stv_eip712_data_word(&**self)
647    }
648
649    #[inline]
650    fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
651        SolTypeValue::<FixedArray<U, N>>::stv_abi_encode_packed_to(&**self, out)
652    }
653
654    #[inline]
655    fn stv_abi_packed_encoded_size(&self) -> usize {
656        SolTypeValue::<FixedArray<U, N>>::stv_abi_packed_encoded_size(&**self)
657    }
658}
659
660impl<T: SolType, const N: usize> SolType for FixedArray<T, N> {
661    type RustType = [T::RustType; N];
662    type Token<'a> = FixedSeqToken<T::Token<'a>, N>;
663
664    const SOL_NAME: &'static str = NameBuffer::new()
665        .write_str(T::SOL_NAME)
666        .write_byte(b'[')
667        .write_usize(N)
668        .write_byte(b']')
669        .as_str();
670    const ENCODED_SIZE: Option<usize> = match T::ENCODED_SIZE {
671        Some(size) => Some(size * N),
672        None => None,
673    };
674    const PACKED_ENCODED_SIZE: Option<usize> = None;
675
676    #[inline]
677    fn valid_token(token: &Self::Token<'_>) -> bool {
678        token.as_array().iter().all(T::valid_token)
679    }
680
681    #[inline]
682    fn detokenize(token: Self::Token<'_>) -> Self::RustType {
683        token.0.map(T::detokenize)
684    }
685}
686
687macro_rules! tuple_encodable_impls {
688    ($count:literal $(($ty:ident $uty:ident)),+) => {
689        #[allow(non_snake_case)]
690        impl<$($ty: SolTypeValue<$uty>, $uty: SolType),+> SolTypeValue<($($uty,)+)> for ($($ty,)+) {
691            #[inline]
692            fn stv_to_tokens(&self) -> <($($uty,)+) as SolType>::Token<'_> {
693                let ($($ty,)+) = self;
694                ($(SolTypeValue::<$uty>::stv_to_tokens($ty),)+)
695            }
696
697            fn stv_abi_encoded_size(&self) -> usize {
698                if let Some(size) = <($($uty,)+) as SolType>::ENCODED_SIZE {
699                    return size
700                }
701
702                let ($($ty,)+) = self;
703                let sum = 0 $( + $ty.stv_abi_encoded_size() )+;
704                if <($($uty,)+) as SolType>::DYNAMIC {
705                    32 + sum
706                } else {
707                    sum
708                }
709            }
710
711            fn stv_abi_encode_packed_to(&self, out: &mut Vec<u8>) {
712                let ($($ty,)+) = self;
713                $(
714                    $ty.stv_abi_encode_packed_to(out);
715                )+
716            }
717
718            fn stv_eip712_data_word(&self) -> Word {
719                let ($($ty,)+) = self;
720                let encoding: [[u8; 32]; $count] = [$(
721                    <$uty as SolType>::eip712_data_word($ty).0,
722                )+];
723                // SAFETY: Flattening [[u8; 32]; $count] to [u8; $count * 32] is valid
724                let encoding: &[u8] = unsafe { core::slice::from_raw_parts(encoding.as_ptr().cast(), $count * 32) };
725                keccak256(encoding).into()
726            }
727
728            fn stv_abi_packed_encoded_size(&self) -> usize {
729                let ($($ty,)+) = self;
730                0 $(+ $ty.stv_abi_packed_encoded_size())+
731            }
732        }
733    };
734}
735
736macro_rules! tuple_impls {
737    ($count:literal $($ty:ident),+) => {
738        #[allow(non_snake_case)]
739        impl<$($ty: SolType,)+> SolType for ($($ty,)+) {
740            type RustType = ($( $ty::RustType, )+);
741            type Token<'a> = ($( $ty::Token<'a>, )+);
742
743            const SOL_NAME: &'static str = NameBuffer::new()
744                .write_byte(b'(')
745                $(
746                .write_str($ty::SOL_NAME)
747                .write_byte(b',')
748                )+
749                .pop() // Remove the last comma
750                .write_byte(b')')
751                .as_str();
752            const ENCODED_SIZE: Option<usize> = 'l: {
753                let mut acc = 0;
754                $(
755                    match <$ty as SolType>::ENCODED_SIZE {
756                        Some(size) => acc += size,
757                        None => break 'l None,
758                    }
759                )+
760                Some(acc)
761            };
762            const PACKED_ENCODED_SIZE: Option<usize> = 'l: {
763                let mut acc = 0;
764                $(
765                    match <$ty as SolType>::PACKED_ENCODED_SIZE {
766                        Some(size) => acc += size,
767                        None => break 'l None,
768                    }
769                )+
770                Some(acc)
771            };
772
773            fn valid_token(token: &Self::Token<'_>) -> bool {
774                let ($($ty,)+) = token;
775                $(<$ty as SolType>::valid_token($ty))&&+
776            }
777
778            fn detokenize(token: Self::Token<'_>) -> Self::RustType {
779                let ($($ty,)+) = token;
780                ($(
781                    <$ty as SolType>::detokenize($ty),
782                )+)
783            }
784        }
785    };
786}
787
788impl SolTypeValue<()> for () {
789    #[inline]
790    fn stv_to_tokens(&self) {}
791
792    #[inline]
793    fn stv_eip712_data_word(&self) -> Word {
794        Word::ZERO
795    }
796
797    #[inline]
798    fn stv_abi_encode_packed_to(&self, _out: &mut Vec<u8>) {}
799}
800
801all_the_tuples!(@double tuple_encodable_impls);
802
803impl SolType for () {
804    type RustType = ();
805    type Token<'a> = ();
806
807    const SOL_NAME: &'static str = "()";
808    const ENCODED_SIZE: Option<usize> = Some(0);
809    const PACKED_ENCODED_SIZE: Option<usize> = Some(0);
810
811    #[inline]
812    fn valid_token((): &()) -> bool {
813        true
814    }
815
816    #[inline]
817    fn detokenize((): ()) -> Self::RustType {}
818}
819
820all_the_tuples!(tuple_impls);
821
822#[allow(unknown_lints, unnameable_types)]
823mod sealed {
824    pub trait Sealed {}
825}
826use sealed::Sealed;
827
828/// Specifies the number of bytes in a [`FixedBytes`] array as a type.
829pub struct ByteCount<const N: usize>;
830
831impl<const N: usize> Sealed for ByteCount<N> {}
832
833/// Statically guarantees that a `FixedBytes` byte count is marked as supported.
834///
835/// This trait is *sealed*: the list of implementors below is total.
836///
837/// Users do not have the ability to mark additional [`ByteCount<N>`] values as
838/// supported. Only `FixedBytes` with supported byte counts are constructable.
839pub trait SupportedFixedBytes: Sealed {
840    /// The name of the `FixedBytes` type: `bytes<N>`
841    const NAME: &'static str;
842}
843
844macro_rules! supported_fixed_bytes {
845    ($($n:literal),+) => {$(
846        impl SupportedFixedBytes for ByteCount<$n> {
847            const NAME: &'static str = concat!("bytes", $n);
848        }
849    )+};
850}
851
852supported_fixed_bytes!(
853    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
854    27, 28, 29, 30, 31, 32
855);
856
857/// Specifies the number of bits in an [`Int`] or [`Uint`] as a type.
858pub struct IntBitCount<const N: usize>;
859
860impl<const N: usize> Sealed for IntBitCount<N> {}
861
862// Declares types with the same traits
863// TODO: Add more traits
864// TODO: Integrate `num_traits` (needs `ruint`)
865macro_rules! declare_int_types {
866    ($($(#[$attr:meta])* type $name:ident;)*) => {$(
867        $(#[$attr])*
868        type $name: Sized + Copy + PartialOrd + Ord + Eq + Hash
869            + Not + BitAnd + BitOr + BitXor
870            + Add + Sub + Mul + Div + Rem
871            + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign
872            + Debug + Display + LowerHex + UpperHex + Octal + Binary;
873    )*};
874}
875
876/// Statically guarantees that a [`Int`] or [`Uint`] bit count is marked as
877/// supported.
878///
879/// This trait is *sealed*: the list of implementors below is total.
880///
881/// Users do not have the ability to mark additional [`IntBitCount<N>`] values
882/// as supported. Only `Int` and `Uint` with supported byte counts are
883/// constructable.
884pub trait SupportedInt: Sealed {
885    declare_int_types! {
886        /// The signed integer Rust representation.
887        type Int;
888
889        /// The unsigned integer Rust representation.
890        type Uint;
891    }
892
893    /// The name of the `Int` type: `int<N>`
894    const INT_NAME: &'static str;
895
896    /// The name of the `Uint` type: `uint<N>`
897    const UINT_NAME: &'static str;
898
899    /// The number of bits in the integer: `BITS`
900    ///
901    /// Note that this is not equal to `Self::Int::BITS`.
902    const BITS: usize;
903
904    /// The number of bytes in the integer: `BITS / 8`
905    const BYTES: usize = Self::BITS / 8;
906
907    /// The difference between the representation's and this integer's bytes:
908    /// `(Self::Int::BITS - Self::BITS) / 8`
909    ///
910    /// E.g.: `word[Self::WORD_MSB - Self::SKIP_BYTES..] == int.to_be_bytes()`
911    const SKIP_BYTES: usize;
912
913    /// The index of the most significant byte in the Word type.
914    ///
915    /// E.g.: `word[Self::WORD_MSB..] == int.to_be_bytes()[Self::SKIP_BYTES..]`
916    const WORD_MSB: usize = 32 - Self::BYTES;
917
918    /// Tokenizes a signed integer.
919    fn tokenize_int(int: Self::Int) -> WordToken;
920    /// Detokenizes a signed integer.
921    fn detokenize_int(token: WordToken) -> Self::Int;
922    /// ABI-encode a signed integer in packed mode.
923    fn encode_packed_to_int(int: Self::Int, out: &mut Vec<u8>);
924
925    /// Tokenizes an unsigned integer.
926    fn tokenize_uint(uint: Self::Uint) -> WordToken;
927    /// Detokenizes an unsigned integer.
928    fn detokenize_uint(token: WordToken) -> Self::Uint;
929    /// ABI-encode an unsigned integer in packed mode.
930    fn encode_packed_to_uint(uint: Self::Uint, out: &mut Vec<u8>);
931}
932
933macro_rules! supported_int {
934    ($($n:literal => $i:ident, $u:ident;)+) => {$(
935        impl SupportedInt for IntBitCount<$n> {
936            type Int = $i;
937            type Uint = $u;
938
939            const UINT_NAME: &'static str = concat!("uint", $n);
940            const INT_NAME: &'static str = concat!("int", $n);
941
942            const BITS: usize = $n;
943            const SKIP_BYTES: usize = (<$i>::BITS as usize - <Self as SupportedInt>::BITS) / 8;
944
945            int_impls2!($i);
946            uint_impls2!($u);
947        }
948    )+};
949}
950
951macro_rules! int_impls {
952    (@primitive_int $ity:ident) => {
953        #[inline]
954        fn tokenize_int(int: $ity) -> WordToken {
955            let mut word = [int.is_negative() as u8 * 0xff; 32];
956            word[Self::WORD_MSB..].copy_from_slice(&int.to_be_bytes()[Self::SKIP_BYTES..]);
957            WordToken::new(word)
958        }
959
960        #[inline]
961        fn detokenize_int(mut token: WordToken) -> $ity {
962            // sign extend bits to ignore
963            let is_negative = token.0[Self::WORD_MSB] & 0x80 == 0x80;
964            let sign_extension = is_negative as u8 * 0xff;
965            token.0[Self::WORD_MSB - Self::SKIP_BYTES..Self::WORD_MSB].fill(sign_extension);
966
967            let s = &token.0[Self::WORD_MSB - Self::SKIP_BYTES..];
968            <$ity>::from_be_bytes(s.try_into().unwrap())
969        }
970
971        #[inline]
972        fn encode_packed_to_int(int: $ity, out: &mut Vec<u8>) {
973            out.extend_from_slice(&int.to_be_bytes()[Self::SKIP_BYTES..]);
974        }
975    };
976    (@primitive_uint $uty:ident) => {
977        #[inline]
978        fn tokenize_uint(uint: $uty) -> WordToken {
979            let mut word = Word::ZERO;
980            word[Self::WORD_MSB..].copy_from_slice(&uint.to_be_bytes()[Self::SKIP_BYTES..]);
981            WordToken(word)
982        }
983
984        #[inline]
985        fn detokenize_uint(mut token: WordToken) -> $uty {
986            // zero out bits to ignore (u24):
987            // mov   byte ptr [rdi + 28], 0
988            // movbe eax, dword ptr [rdi + 28]
989            token.0[Self::WORD_MSB - Self::SKIP_BYTES..Self::WORD_MSB].fill(0);
990            let s = &token.0[Self::WORD_MSB - Self::SKIP_BYTES..];
991            <$uty>::from_be_bytes(s.try_into().unwrap())
992        }
993
994        #[inline]
995        fn encode_packed_to_uint(uint: $uty, out: &mut Vec<u8>) {
996            out.extend_from_slice(&uint.to_be_bytes()[Self::SKIP_BYTES..]);
997        }
998    };
999
1000    (@big_int $ity:ident) => {
1001        #[inline]
1002        fn tokenize_int(int: $ity) -> WordToken {
1003            let mut word = [int.is_negative() as u8 * 0xff; 32];
1004            word[Self::WORD_MSB..]
1005                .copy_from_slice(&int.to_be_bytes::<{ $ity::BYTES }>()[Self::SKIP_BYTES..]);
1006            WordToken::new(word)
1007        }
1008
1009        #[inline]
1010        fn detokenize_int(mut token: WordToken) -> $ity {
1011            // sign extend bits to ignore
1012            let is_negative = token.0[Self::WORD_MSB] & 0x80 == 0x80;
1013            let sign_extension = is_negative as u8 * 0xff;
1014            token.0[Self::WORD_MSB - Self::SKIP_BYTES..Self::WORD_MSB].fill(sign_extension);
1015
1016            let s = &token.0[Self::WORD_MSB - Self::SKIP_BYTES..];
1017            <$ity>::from_be_bytes::<{ $ity::BYTES }>(s.try_into().unwrap())
1018        }
1019
1020        #[inline]
1021        fn encode_packed_to_int(int: $ity, out: &mut Vec<u8>) {
1022            out.extend_from_slice(&int.to_be_bytes::<{ $ity::BYTES }>()[Self::SKIP_BYTES..]);
1023        }
1024    };
1025    (@big_uint $uty:ident) => {
1026        #[inline]
1027        fn tokenize_uint(uint: $uty) -> WordToken {
1028            let mut word = Word::ZERO;
1029            word[Self::WORD_MSB..]
1030                .copy_from_slice(&uint.to_be_bytes::<{ $uty::BYTES }>()[Self::SKIP_BYTES..]);
1031            WordToken(word)
1032        }
1033
1034        #[inline]
1035        fn detokenize_uint(mut token: WordToken) -> $uty {
1036            // zero out bits to ignore
1037            token.0[..Self::SKIP_BYTES].fill(0);
1038            let s = &token.0[Self::WORD_MSB - Self::SKIP_BYTES..];
1039            <$uty>::from_be_bytes::<{ $uty::BYTES }>(s.try_into().unwrap())
1040        }
1041
1042        #[inline]
1043        fn encode_packed_to_uint(uint: $uty, out: &mut Vec<u8>) {
1044            out.extend_from_slice(&uint.to_be_bytes::<{ $uty::BYTES }>()[Self::SKIP_BYTES..]);
1045        }
1046    };
1047}
1048
1049#[rustfmt::skip]
1050macro_rules! int_impls2 {
1051    (  i8) => { int_impls! { @primitive_int    i8 } };
1052    ( i16) => { int_impls! { @primitive_int   i16 } };
1053    ( i32) => { int_impls! { @primitive_int   i32 } };
1054    ( i64) => { int_impls! { @primitive_int   i64 } };
1055    (i128) => { int_impls! { @primitive_int  i128 } };
1056
1057    ($t:ident) => { int_impls! { @big_int $t } };
1058}
1059
1060#[rustfmt::skip]
1061macro_rules! uint_impls2 {
1062    (  u8) => { int_impls! { @primitive_uint   u8 } };
1063    ( u16) => { int_impls! { @primitive_uint  u16 } };
1064    ( u32) => { int_impls! { @primitive_uint  u32 } };
1065    ( u64) => { int_impls! { @primitive_uint  u64 } };
1066    (u128) => { int_impls! { @primitive_uint u128 } };
1067
1068    ($t:ident) => { int_impls! { @big_uint $t } };
1069}
1070
1071supported_int!(
1072      8 =>   i8,   u8;
1073     16 =>  i16,  u16;
1074     24 =>  I24,  U24;
1075     32 =>  i32,  u32;
1076     40 =>  I40,  U40;
1077     48 =>  I48,  U48;
1078     56 =>  I56,  U56;
1079     64 =>  i64,  u64;
1080     72 =>  I72,  U72;
1081     80 =>  I80,  U80;
1082     88 =>  I88,  U88;
1083     96 =>  I96,  U96;
1084    104 => I104, U104;
1085    112 => I112, U112;
1086    120 => I120, U120;
1087    128 => i128, u128;
1088    136 => I136, U136;
1089    144 => I144, U144;
1090    152 => I152, U152;
1091    160 => I160, U160;
1092    168 => I168, U168;
1093    176 => I176, U176;
1094    184 => I184, U184;
1095    192 => I192, U192;
1096    200 => I200, U200;
1097    208 => I208, U208;
1098    216 => I216, U216;
1099    224 => I224, U224;
1100    232 => I232, U232;
1101    240 => I240, U240;
1102    248 => I248, U248;
1103    256 => I256, U256;
1104);
1105
1106const NAME_CAP: usize = 256;
1107
1108/// Simple buffer for constructing strings at compile time.
1109#[must_use]
1110struct NameBuffer {
1111    buffer: [u8; NAME_CAP],
1112    len: usize,
1113}
1114
1115impl NameBuffer {
1116    const fn new() -> Self {
1117        Self { buffer: [0; NAME_CAP], len: 0 }
1118    }
1119
1120    const fn write_str(self, s: &str) -> Self {
1121        self.write_bytes(s.as_bytes())
1122    }
1123
1124    const fn write_bytes(mut self, s: &[u8]) -> Self {
1125        let mut i = 0;
1126        while i < s.len() {
1127            self.buffer[self.len + i] = s[i];
1128            i += 1;
1129        }
1130        self.len += s.len();
1131        self
1132    }
1133
1134    const fn write_byte(mut self, b: u8) -> Self {
1135        self.buffer[self.len] = b;
1136        self.len += 1;
1137        self
1138    }
1139
1140    const fn write_usize(mut self, number: usize) -> Self {
1141        let Some(digits) = number.checked_ilog10() else {
1142            return self.write_byte(b'0');
1143        };
1144        let digits = digits as usize + 1;
1145
1146        let mut n = number;
1147        let mut i = self.len + digits;
1148        while n > 0 {
1149            i -= 1;
1150            self.buffer[i] = b'0' + (n % 10) as u8;
1151            n /= 10;
1152        }
1153        self.len += digits;
1154
1155        self
1156    }
1157
1158    const fn pop(mut self) -> Self {
1159        self.len -= 1;
1160        self
1161    }
1162
1163    const fn as_bytes(&self) -> &[u8] {
1164        assert!(self.len <= self.buffer.len());
1165        unsafe { core::slice::from_raw_parts(self.buffer.as_ptr(), self.len) }
1166    }
1167
1168    const fn as_str(&self) -> &str {
1169        match core::str::from_utf8(self.as_bytes()) {
1170            Ok(s) => s,
1171            Err(_) => panic!("wrote invalid UTF-8"),
1172        }
1173    }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178    use super::*;
1179    use crate::{SolValue, sol};
1180    use alloy_primitives::{Signed, hex};
1181
1182    #[test]
1183    fn sol_names() {
1184        macro_rules! assert_name {
1185            ($t:ty, $s:literal) => {
1186                assert_eq!(<$t as SolType>::SOL_NAME, $s);
1187            };
1188        }
1189
1190        assert_name!(Bool, "bool");
1191        assert_name!(Uint<8>, "uint8");
1192        assert_name!(Uint<16>, "uint16");
1193        assert_name!(Uint<32>, "uint32");
1194        assert_name!(Int<8>, "int8");
1195        assert_name!(Int<16>, "int16");
1196        assert_name!(Int<32>, "int32");
1197        assert_name!(FixedBytes<1>, "bytes1");
1198        assert_name!(FixedBytes<16>, "bytes16");
1199        assert_name!(FixedBytes<32>, "bytes32");
1200        assert_name!(Address, "address");
1201        assert_name!(Function, "function");
1202        assert_name!(Bytes, "bytes");
1203        assert_name!(String, "string");
1204
1205        assert_name!(Array<Uint<8>>, "uint8[]");
1206        assert_name!(Array<Bytes>, "bytes[]");
1207        assert_name!(FixedArray<Uint<8>, 0>, "uint8[0]");
1208        assert_name!(FixedArray<Uint<8>, 1>, "uint8[1]");
1209        assert_name!(FixedArray<Uint<8>, 2>, "uint8[2]");
1210        assert_name!((), "()");
1211        assert_name!((Uint<8>,), "(uint8)");
1212        assert_name!((Uint<8>, Bool), "(uint8,bool)");
1213        assert_name!((Uint<8>, Bool, FixedArray<Address, 4>), "(uint8,bool,address[4])");
1214    }
1215
1216    macro_rules! assert_encoded_size {
1217        ($t:ty, $sz:expr) => {
1218            let sz = $sz;
1219            assert_eq!(<$t as SolType>::ENCODED_SIZE, sz);
1220            assert_eq!(<$t as SolType>::DYNAMIC, sz.is_none());
1221        };
1222    }
1223
1224    #[test]
1225    fn primitive_encoded_sizes() {
1226        assert_encoded_size!(Bool, Some(32));
1227
1228        assert_encoded_size!(Uint<8>, Some(32));
1229        assert_encoded_size!(Int<8>, Some(32));
1230        assert_encoded_size!(Uint<16>, Some(32));
1231        assert_encoded_size!(Int<16>, Some(32));
1232        assert_encoded_size!(Uint<32>, Some(32));
1233        assert_encoded_size!(Int<32>, Some(32));
1234        assert_encoded_size!(Uint<64>, Some(32));
1235        assert_encoded_size!(Int<64>, Some(32));
1236        assert_encoded_size!(Uint<128>, Some(32));
1237        assert_encoded_size!(Int<128>, Some(32));
1238        assert_encoded_size!(Uint<256>, Some(32));
1239        assert_encoded_size!(Int<256>, Some(32));
1240
1241        assert_encoded_size!(Address, Some(32));
1242        assert_encoded_size!(Function, Some(32));
1243        assert_encoded_size!(FixedBytes<1>, Some(32));
1244        assert_encoded_size!(FixedBytes<16>, Some(32));
1245        assert_encoded_size!(FixedBytes<32>, Some(32));
1246
1247        assert_encoded_size!(Bytes, None);
1248        assert_encoded_size!(String, None);
1249
1250        assert_encoded_size!(Array<()>, None);
1251        assert_encoded_size!(Array<Uint<8>>, None);
1252        assert_encoded_size!(Array<Bytes>, None);
1253
1254        assert_encoded_size!(FixedArray<(), 0>, Some(0));
1255        assert_encoded_size!(FixedArray<(), 1>, Some(0));
1256        assert_encoded_size!(FixedArray<(), 2>, Some(0));
1257        assert_encoded_size!(FixedArray<Uint<8>, 0>, Some(0));
1258        assert_encoded_size!(FixedArray<Uint<8>, 1>, Some(32));
1259        assert_encoded_size!(FixedArray<Uint<8>, 2>, Some(64));
1260        assert_encoded_size!(FixedArray<Bytes, 0>, None);
1261        assert_encoded_size!(FixedArray<Bytes, 1>, None);
1262        assert_encoded_size!(FixedArray<Bytes, 2>, None);
1263
1264        assert_encoded_size!((), Some(0));
1265        assert_encoded_size!(((),), Some(0));
1266        assert_encoded_size!(((), ()), Some(0));
1267        assert_encoded_size!((Uint<8>,), Some(32));
1268        assert_encoded_size!((Uint<8>, Bool), Some(64));
1269        assert_encoded_size!((Uint<8>, Bool, FixedArray<Address, 4>), Some(6 * 32));
1270        assert_encoded_size!((Bytes,), None);
1271        assert_encoded_size!((Uint<8>, Bytes), None);
1272    }
1273
1274    #[test]
1275    fn udvt_encoded_sizes() {
1276        macro_rules! udvt_and_assert {
1277            ([$($t:tt)*], $e:expr) => {{
1278                type Alias = sol!($($t)*);
1279                sol!(type Udvt is $($t)*;);
1280                assert_encoded_size!(Alias, $e);
1281                assert_encoded_size!(Udvt, $e);
1282            }};
1283        }
1284        udvt_and_assert!([bool], Some(32));
1285
1286        udvt_and_assert!([uint8], Some(32));
1287        udvt_and_assert!([int8], Some(32));
1288        udvt_and_assert!([uint16], Some(32));
1289        udvt_and_assert!([int16], Some(32));
1290        udvt_and_assert!([uint32], Some(32));
1291        udvt_and_assert!([int32], Some(32));
1292        udvt_and_assert!([uint64], Some(32));
1293        udvt_and_assert!([int64], Some(32));
1294        udvt_and_assert!([uint128], Some(32));
1295        udvt_and_assert!([int128], Some(32));
1296        udvt_and_assert!([uint256], Some(32));
1297        udvt_and_assert!([int256], Some(32));
1298
1299        udvt_and_assert!([address], Some(32));
1300        udvt_and_assert!([function()], Some(32));
1301        udvt_and_assert!([bytes1], Some(32));
1302        udvt_and_assert!([bytes16], Some(32));
1303        udvt_and_assert!([bytes32], Some(32));
1304    }
1305
1306    #[test]
1307    fn custom_encoded_sizes() {
1308        macro_rules! custom_and_assert {
1309            ($block:tt, $e:expr) => {{
1310                sol! {
1311                    struct Struct $block
1312                }
1313                assert_encoded_size!(Struct, $e);
1314            }};
1315        }
1316        custom_and_assert!({ bool a; }, Some(32));
1317        custom_and_assert!({ bool a; address b; }, Some(64));
1318        custom_and_assert!({ bool a; bytes1[69] b; uint8 c; }, Some(71 * 32));
1319        custom_and_assert!({ bytes a; }, None);
1320        custom_and_assert!({ bytes a; bytes24 b; }, None);
1321        custom_and_assert!({ bool a; bytes2[42] b; uint8 c; bytes d; }, None);
1322    }
1323
1324    #[test]
1325    fn tuple_of_refs() {
1326        let a = (1u8,);
1327        let b = (&1u8,);
1328
1329        type MyTy = (Uint<8>,);
1330
1331        MyTy::tokenize(&a);
1332        MyTy::tokenize(&b);
1333    }
1334
1335    macro_rules! roundtrip {
1336        ($($name:ident($st:ty : $t:ty);)+) => {
1337            proptest::proptest! {$(
1338                #[test]
1339                #[cfg_attr(miri, ignore = "doesn't run in isolation and would take too long")]
1340                fn $name(i: $t) {
1341                    let token = <$st>::tokenize(&i);
1342                    proptest::prop_assert_eq!(token.total_words() * 32, <$st>::abi_encoded_size(&i));
1343                    proptest::prop_assert_eq!(<$st>::detokenize(token), i);
1344                }
1345            )+}
1346        };
1347    }
1348
1349    roundtrip! {
1350        roundtrip_address(Address: RustAddress);
1351        roundtrip_bool(Bool: bool);
1352        roundtrip_bytes(Bytes: Vec<u8>);
1353        roundtrip_string(String: RustString);
1354        roundtrip_fixed_bytes_16(FixedBytes<16>: [u8; 16]);
1355        roundtrip_fixed_bytes_32(FixedBytes<32>: [u8; 32]);
1356
1357        // can only test corresponding integers
1358        roundtrip_u8(Uint<8>: u8);
1359        roundtrip_i8(Int<8>: i8);
1360        roundtrip_u16(Uint<16>: u16);
1361        roundtrip_i16(Int<16>: i16);
1362        roundtrip_u32(Uint<32>: u32);
1363        roundtrip_i32(Int<32>: i32);
1364        roundtrip_u64(Uint<64>: u64);
1365        roundtrip_i64(Int<64>: i64);
1366        roundtrip_u128(Uint<128>: u128);
1367        roundtrip_i128(Int<128>: i128);
1368        roundtrip_u256(Uint<256>: U256);
1369        roundtrip_i256(Int<256>: I256);
1370    }
1371
1372    #[test]
1373    fn tokenize_uint() {
1374        macro_rules! test {
1375            ($($n:literal: $x:expr => $l:literal),+ $(,)?) => {$(
1376                let uint = <Uint<$n> as SolType>::RustType::try_from($x).unwrap();
1377                let int = <Int<$n> as SolType>::RustType::try_from(uint).unwrap();
1378
1379                assert_eq!(
1380                    <Uint<$n>>::tokenize(&uint),
1381                    WordToken::new(alloy_primitives::hex!($l))
1382                );
1383                assert_eq!(
1384                    <Int<$n>>::tokenize(&int),
1385                    WordToken::new(alloy_primitives::hex!($l))
1386                );
1387            )+};
1388        }
1389
1390        let word = core::array::from_fn::<_, 32, _>(|i| i as u8 + 1);
1391
1392        test! {
1393             8: 0x00u8 => "0000000000000000000000000000000000000000000000000000000000000000",
1394             8: 0x01u8 => "0000000000000000000000000000000000000000000000000000000000000001",
1395            24: 0x00020304u32 => "0000000000000000000000000000000000000000000000000000000000020304",
1396            32: 0x01020304u32 => "0000000000000000000000000000000000000000000000000000000001020304",
1397            56: 0x0002030405060708u64 => "0000000000000000000000000000000000000000000000000002030405060708",
1398            64: 0x0102030405060708u64 => "0000000000000000000000000000000000000000000000000102030405060708",
1399
1400            160: U160::from_be_slice(&word[32 - 160/8..]) => "0000000000000000000000000d0e0f101112131415161718191a1b1c1d1e1f20",
1401            200: U200::from_be_slice(&word[32 - 200/8..]) => "0000000000000008090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
1402            256: U256::from_be_slice(&word[32 - 256/8..]) => "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
1403        }
1404    }
1405
1406    #[test]
1407    fn detokenize_ints() {
1408        /*
1409        for i in range(1, 32 + 1):
1410            n = "0x"
1411            for j in range(32, 0, -1):
1412                if j <= i:
1413                    n += hex(33 - j)[2:].zfill(2)
1414                else:
1415                    n += "00"
1416            if i > 16:
1417                n = f'"{n}".parse().unwrap()'
1418            else:
1419                n = f" {n}"
1420            print(f"{i * 8:4} => {n},")
1421        */
1422        let word = core::array::from_fn(|i| i as u8 + 1);
1423        let token = WordToken::new(word);
1424        macro_rules! test {
1425            ($($n:literal => $x:expr),+ $(,)?) => {$(
1426                assert_eq!(<Uint<$n>>::detokenize(token), $x);
1427                assert_eq!(<Int<$n>>::detokenize(token), $x);
1428            )+};
1429        }
1430        #[rustfmt::skip]
1431        test! {
1432             8 =>  0x0000000000000000000000000000000000000000000000000000000000000020,
1433            16 =>  0x0000000000000000000000000000000000000000000000000000000000001f20,
1434            24 => "0x00000000000000000000000000000000000000000000000000000000001e1f20".parse().unwrap(),
1435            32 =>  0x000000000000000000000000000000000000000000000000000000001d1e1f20,
1436            40 => "0x0000000000000000000000000000000000000000000000000000001c1d1e1f20".parse().unwrap(),
1437            48 => "0x00000000000000000000000000000000000000000000000000001b1c1d1e1f20".parse().unwrap(),
1438            56 => "0x000000000000000000000000000000000000000000000000001a1b1c1d1e1f20".parse().unwrap(),
1439            64 =>  0x000000000000000000000000000000000000000000000000191a1b1c1d1e1f20,
1440            72 => "0x000000000000000000000000000000000000000000000018191a1b1c1d1e1f20".parse().unwrap(),
1441            80 => "0x000000000000000000000000000000000000000000001718191a1b1c1d1e1f20".parse().unwrap(),
1442            88 => "0x000000000000000000000000000000000000000000161718191a1b1c1d1e1f20".parse().unwrap(),
1443            96 => "0x000000000000000000000000000000000000000015161718191a1b1c1d1e1f20".parse().unwrap(),
1444           104 => "0x000000000000000000000000000000000000001415161718191a1b1c1d1e1f20".parse().unwrap(),
1445           112 => "0x000000000000000000000000000000000000131415161718191a1b1c1d1e1f20".parse().unwrap(),
1446           120 => "0x000000000000000000000000000000000012131415161718191a1b1c1d1e1f20".parse().unwrap(),
1447           128 =>  0x000000000000000000000000000000001112131415161718191a1b1c1d1e1f20,
1448           136 => "0x000000000000000000000000000000101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1449           144 => "0x00000000000000000000000000000f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1450           152 => "0x000000000000000000000000000e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1451           160 => "0x0000000000000000000000000d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1452           168 => "0x00000000000000000000000c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1453           176 => "0x000000000000000000000b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1454           184 => "0x0000000000000000000a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1455           192 => "0x0000000000000000090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1456           200 => "0x0000000000000008090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1457           208 => "0x0000000000000708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1458           216 => "0x0000000000060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1459           224 => "0x0000000005060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1460           232 => "0x0000000405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1461           240 => "0x0000030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1462           248 => "0x0002030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1463           256 => "0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20".parse().unwrap(),
1464        };
1465    }
1466
1467    #[test]
1468    fn detokenize_negative_int() {
1469        let word = [0xff; 32];
1470        let token = WordToken::new(word);
1471        assert_eq!(<Int<8>>::detokenize(token), -1);
1472        assert_eq!(<Int<16>>::detokenize(token), -1);
1473        assert_eq!(<Int<24>>::detokenize(token), Signed::MINUS_ONE);
1474        assert_eq!(<Int<32>>::detokenize(token), -1);
1475        assert_eq!(<Int<40>>::detokenize(token), Signed::MINUS_ONE);
1476        assert_eq!(<Int<48>>::detokenize(token), Signed::MINUS_ONE);
1477        assert_eq!(<Int<56>>::detokenize(token), Signed::MINUS_ONE);
1478        assert_eq!(<Int<64>>::detokenize(token), -1);
1479        assert_eq!(<Int<72>>::detokenize(token), Signed::MINUS_ONE);
1480        assert_eq!(<Int<80>>::detokenize(token), Signed::MINUS_ONE);
1481        assert_eq!(<Int<88>>::detokenize(token), Signed::MINUS_ONE);
1482        assert_eq!(<Int<96>>::detokenize(token), Signed::MINUS_ONE);
1483        assert_eq!(<Int<104>>::detokenize(token), Signed::MINUS_ONE);
1484        assert_eq!(<Int<112>>::detokenize(token), Signed::MINUS_ONE);
1485        assert_eq!(<Int<120>>::detokenize(token), Signed::MINUS_ONE);
1486        assert_eq!(<Int<128>>::detokenize(token), -1);
1487        assert_eq!(<Int<136>>::detokenize(token), Signed::MINUS_ONE);
1488        assert_eq!(<Int<144>>::detokenize(token), Signed::MINUS_ONE);
1489        assert_eq!(<Int<152>>::detokenize(token), Signed::MINUS_ONE);
1490        assert_eq!(<Int<160>>::detokenize(token), Signed::MINUS_ONE);
1491        assert_eq!(<Int<168>>::detokenize(token), Signed::MINUS_ONE);
1492        assert_eq!(<Int<176>>::detokenize(token), Signed::MINUS_ONE);
1493        assert_eq!(<Int<184>>::detokenize(token), Signed::MINUS_ONE);
1494        assert_eq!(<Int<192>>::detokenize(token), Signed::MINUS_ONE);
1495        assert_eq!(<Int<200>>::detokenize(token), Signed::MINUS_ONE);
1496        assert_eq!(<Int<208>>::detokenize(token), Signed::MINUS_ONE);
1497        assert_eq!(<Int<216>>::detokenize(token), Signed::MINUS_ONE);
1498        assert_eq!(<Int<224>>::detokenize(token), Signed::MINUS_ONE);
1499        assert_eq!(<Int<232>>::detokenize(token), Signed::MINUS_ONE);
1500        assert_eq!(<Int<240>>::detokenize(token), Signed::MINUS_ONE);
1501        assert_eq!(<Int<248>>::detokenize(token), Signed::MINUS_ONE);
1502        assert_eq!(<Int<256>>::detokenize(token), Signed::MINUS_ONE);
1503    }
1504
1505    #[test]
1506    #[rustfmt::skip]
1507    fn detokenize_int() {
1508        use alloy_primitives::Uint;
1509
1510        let word =
1511            core::array::from_fn(|i| (i | (0x80 * (i % 2 == 1) as usize)) as u8 + 1);
1512        let token = WordToken::new(word);
1513        trait Conv<const BITS: usize, const LIMBS: usize> {
1514            fn as_uint_as_int(&self) -> Signed<BITS, LIMBS>;
1515        }
1516        impl<const BITS: usize, const LIMBS: usize> Conv<BITS, LIMBS> for str {
1517            fn as_uint_as_int(&self) -> Signed<BITS, LIMBS> {
1518                Signed::<BITS, LIMBS>::from_raw(self.parse::<Uint<BITS, LIMBS>>().unwrap())
1519            }
1520        }
1521        assert_eq!(<Int<8>>::detokenize(token),    0x00000000000000000000000000000000000000000000000000000000000000a0_u8 as i8);
1522        assert_eq!(<Int<16>>::detokenize(token),   0x0000000000000000000000000000000000000000000000000000000000001fa0_u16 as i16);
1523        assert_eq!(<Int<24>>::detokenize(token),  "0x00000000000000000000000000000000000000000000000000000000009e1fa0".as_uint_as_int());
1524        assert_eq!(<Int<32>>::detokenize(token),   0x000000000000000000000000000000000000000000000000000000001d9e1fa0_u32 as i32);
1525        assert_eq!(<Int<40>>::detokenize(token),  "0x0000000000000000000000000000000000000000000000000000009c1d9e1fa0".as_uint_as_int());
1526        assert_eq!(<Int<48>>::detokenize(token),  "0x00000000000000000000000000000000000000000000000000001b9c1d9e1fa0".as_uint_as_int());
1527        assert_eq!(<Int<56>>::detokenize(token),  "0x000000000000000000000000000000000000000000000000009a1b9c1d9e1fa0".as_uint_as_int());
1528        assert_eq!(<Int<64>>::detokenize(token),   0x000000000000000000000000000000000000000000000000199a1b9c1d9e1fa0_u64 as i64);
1529        assert_eq!(<Int<72>>::detokenize(token),  "0x000000000000000000000000000000000000000000000098199a1b9c1d9e1fa0".as_uint_as_int());
1530        assert_eq!(<Int<80>>::detokenize(token),  "0x000000000000000000000000000000000000000000001798199a1b9c1d9e1fa0".as_uint_as_int());
1531        assert_eq!(<Int<88>>::detokenize(token),  "0x000000000000000000000000000000000000000000961798199a1b9c1d9e1fa0".as_uint_as_int());
1532        assert_eq!(<Int<96>>::detokenize(token),  "0x000000000000000000000000000000000000000015961798199a1b9c1d9e1fa0".as_uint_as_int());
1533        assert_eq!(<Int<104>>::detokenize(token), "0x000000000000000000000000000000000000009415961798199a1b9c1d9e1fa0".as_uint_as_int());
1534        assert_eq!(<Int<112>>::detokenize(token), "0x000000000000000000000000000000000000139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1535        assert_eq!(<Int<120>>::detokenize(token), "0x000000000000000000000000000000000092139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1536        assert_eq!(<Int<128>>::detokenize(token),  0x000000000000000000000000000000001192139415961798199a1b9c1d9e1fa0_u128 as i128);
1537        assert_eq!(<Int<136>>::detokenize(token), "0x000000000000000000000000000000901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1538        assert_eq!(<Int<144>>::detokenize(token), "0x00000000000000000000000000000f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1539        assert_eq!(<Int<152>>::detokenize(token), "0x000000000000000000000000008e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1540        assert_eq!(<Int<160>>::detokenize(token), "0x0000000000000000000000000d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1541        assert_eq!(<Int<168>>::detokenize(token), "0x00000000000000000000008c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1542        assert_eq!(<Int<176>>::detokenize(token), "0x000000000000000000000b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1543        assert_eq!(<Int<184>>::detokenize(token), "0x0000000000000000008a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1544        assert_eq!(<Int<192>>::detokenize(token), "0x0000000000000000098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1545        assert_eq!(<Int<200>>::detokenize(token), "0x0000000000000088098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1546        assert_eq!(<Int<208>>::detokenize(token), "0x0000000000000788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1547        assert_eq!(<Int<216>>::detokenize(token), "0x0000000000860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1548        assert_eq!(<Int<224>>::detokenize(token), "0x0000000005860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1549        assert_eq!(<Int<232>>::detokenize(token), "0x0000008405860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1550        assert_eq!(<Int<240>>::detokenize(token), "0x0000038405860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1551        assert_eq!(<Int<248>>::detokenize(token), "0x0082038405860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1552        assert_eq!(<Int<256>>::detokenize(token), "0x0182038405860788098a0b8c0d8e0f901192139415961798199a1b9c1d9e1fa0".as_uint_as_int());
1553    }
1554
1555    #[test]
1556    fn encode_packed() {
1557        use alloy_primitives::Uint;
1558
1559        let value = (
1560            RustAddress::with_last_byte(1),
1561            Uint::<160, 3>::from(2),
1562            Uint::from(3u32),
1563            Signed::unchecked_from(-3i32),
1564            3u32,
1565            -3i32,
1566        );
1567
1568        let res_ty =
1569            <sol! { (address, uint160, uint24, int24, uint32, int32) }>::abi_encode_packed(&value);
1570        let res_value = value.abi_encode_packed();
1571        let expected = hex!(
1572            "0000000000000000000000000000000000000001"
1573            "0000000000000000000000000000000000000002"
1574            "000003"
1575            "fffffd"
1576            "00000003"
1577            "fffffffd"
1578        );
1579        assert_eq!(hex::encode(res_ty), hex::encode(expected));
1580        assert_eq!(hex::encode(res_value), hex::encode(expected));
1581    }
1582}