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