Skip to main content

alloy_sol_types/abi/
token.rs

1// Copyright 2015-2020 Parity Technologies
2// Copyright 2023-2023 Alloy Contributors
3
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Ethereum ABI tokens.
11//!
12//! See [`Token`] for more details.
13
14use crate::{
15    Result, Word,
16    abi::{Decoder, Encoder},
17};
18use alloc::vec::Vec;
19use alloy_primitives::{Bytes, FixedBytes, I256, U256, hex, utils::vec_try_with_capacity};
20use core::{fmt, mem, mem::MaybeUninit, ptr};
21
22#[allow(unknown_lints, unnameable_types)]
23mod sealed {
24    pub trait Sealed {}
25    impl Sealed for super::WordToken {}
26    impl Sealed for () {}
27    impl<T, const N: usize> Sealed for super::FixedSeqToken<T, N> {}
28    impl<T> Sealed for super::DynSeqToken<T> {}
29    impl Sealed for super::PackedSeqToken<'_> {}
30}
31use sealed::Sealed;
32
33/// Ethereum ABI tokens.
34///
35/// Tokens are an intermediate state between ABI-encoded blobs, and Rust types.
36///
37/// ABI encoding uses 5 types:
38/// - [`WordToken`]: Single EVM words (a 32-byte string)
39/// - [`FixedSeqToken`]: Sequences with a fixed length `T[M]`
40/// - [`DynSeqToken`]: Sequences with a dynamic length `T[]`
41/// - [`PackedSeqToken`]: Dynamic-length byte arrays `bytes` or `string`
42/// - Tuples `(T, U, V, ...)` (implemented for arity `0..=24`)
43///
44/// A token with a lifetime borrows its data from elsewhere. During decoding,
45/// it borrows its data from the decoder. During encoding, it borrows its data
46/// from the Rust value being encoded.
47///
48/// This trait allows us to encode and decode data with minimal copying. It may
49/// also be used to enable zero-copy decoding of data, or fast transformation of
50/// encoded blobs without full decoding.
51///
52/// This trait is sealed and cannot be implemented for types outside of this
53/// crate. It is implemented only for the types listed above.
54pub trait Token<'de>: Sealed + Sized {
55    /// True if the token represents a dynamically-sized type.
56    const DYNAMIC: bool;
57
58    /// Decode a token from a decoder.
59    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self>;
60
61    /// Decode tokens from a decoder into the given uninitialized buffer.
62    ///
63    /// On success, returns the initialized slice.
64    /// On error, no elements are initialized (partially initialized elements are dropped).
65    ///
66    /// The default implementation simply loops over [`decode_from`](Self::decode_from).
67    /// Implementations may override this to provide a more efficient batch decode,
68    /// e.g. a single `memcpy` for [`WordToken`].
69    ///
70    /// # Safety
71    ///
72    /// `out` must point to valid, writable memory for `out.len()` elements.
73    #[inline]
74    unsafe fn decode_many_from<'a>(
75        dec: &mut Decoder<'de>,
76        out: &'a mut [MaybeUninit<Self>],
77    ) -> Result<&'a mut [Self]> {
78        try_init_each(out, || Self::decode_from(dec))
79    }
80
81    /// Calculate the number of head words.
82    fn head_words(&self) -> usize;
83
84    /// Calculate the number of tail words.
85    fn tail_words(&self) -> usize;
86
87    /// Calculate the total number of head and tail words.
88    #[inline]
89    fn total_words(&self) -> usize {
90        self.head_words() + self.tail_words()
91    }
92
93    /// Append head words to the encoder.
94    fn head_append(&self, enc: &mut Encoder);
95
96    /// Append head words for a slice of tokens to the encoder.
97    ///
98    /// The default implementation simply loops over [`head_append`](Self::head_append).
99    /// Implementations may override this to provide a more efficient batch encode,
100    /// e.g. a single `memcpy` for [`WordToken`].
101    #[inline]
102    fn head_append_many(tokens: &[Self], enc: &mut Encoder) {
103        for token in tokens {
104            token.head_append(enc);
105        }
106    }
107
108    /// Append tail words to the encoder.
109    fn tail_append(&self, enc: &mut Encoder);
110}
111
112/// A token composed of a sequence of other tokens.
113///
114/// This functions is an extension trait for [`Token`], and is only
115/// implemented by [`FixedSeqToken`], [`DynSeqToken`], [`PackedSeqToken`], and
116/// tuples of [`Token`]s (including [`WordToken`]).
117pub trait TokenSeq<'a>: Token<'a> {
118    /// True for tuples only.
119    const IS_TUPLE: bool = false;
120
121    /// ABI-encode the token sequence into the encoder.
122    fn encode_sequence(&self, enc: &mut Encoder);
123
124    /// ABI-decode the token sequence from the encoder.
125    fn decode_sequence(dec: &mut Decoder<'a>) -> Result<Self>;
126}
127
128/// A single EVM word - T for any value type.
129#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
130#[repr(transparent)]
131pub struct WordToken(pub Word);
132
133impl<T> From<&T> for WordToken
134where
135    T: Clone,
136    Self: From<T>,
137{
138    #[inline]
139    fn from(value: &T) -> Self {
140        Self::from(value.clone())
141    }
142}
143
144impl<T> From<&mut T> for WordToken
145where
146    T: Clone,
147    Self: From<T>,
148{
149    #[inline]
150    fn from(value: &mut T) -> Self {
151        Self::from(value.clone())
152    }
153}
154
155impl From<Word> for WordToken {
156    #[inline]
157    fn from(value: Word) -> Self {
158        Self(value)
159    }
160}
161
162impl From<WordToken> for Word {
163    #[inline]
164    fn from(value: WordToken) -> Self {
165        value.0
166    }
167}
168
169impl From<bool> for WordToken {
170    #[inline]
171    fn from(value: bool) -> Self {
172        U256::from(value as u64).into()
173    }
174}
175
176impl From<U256> for WordToken {
177    #[inline]
178    fn from(value: U256) -> Self {
179        Self(value.into())
180    }
181}
182
183impl From<I256> for WordToken {
184    #[inline]
185    fn from(value: I256) -> Self {
186        Self(value.into())
187    }
188}
189
190impl From<WordToken> for [u8; 32] {
191    #[inline]
192    fn from(value: WordToken) -> [u8; 32] {
193        value.0.into()
194    }
195}
196
197impl From<[u8; 32]> for WordToken {
198    #[inline]
199    fn from(value: [u8; 32]) -> Self {
200        Self(value.into())
201    }
202}
203
204impl AsRef<Word> for WordToken {
205    #[inline]
206    fn as_ref(&self) -> &Word {
207        &self.0
208    }
209}
210
211impl AsRef<[u8]> for WordToken {
212    #[inline]
213    fn as_ref(&self) -> &[u8] {
214        &self.0.0
215    }
216}
217
218impl<'a> Token<'a> for WordToken {
219    const DYNAMIC: bool = false;
220
221    #[inline]
222    fn decode_from(dec: &mut Decoder<'a>) -> Result<Self> {
223        dec.take_word().copied().map(Self)
224    }
225
226    #[inline]
227    unsafe fn decode_many_from<'b>(
228        dec: &mut Decoder<'a>,
229        out: &'b mut [MaybeUninit<Self>],
230    ) -> Result<&'b mut [Self]> {
231        let len = out.len();
232        let byte_len = len * Word::len_bytes();
233        let slice = dec.take_slice(byte_len)?;
234        // SAFETY: `MaybeUninit<WordToken>` has the same layout as `WordToken` which is
235        // `#[repr(transparent)]` over `Word` (`[u8; 32]`), all with alignment 1.
236        // `slice` is exactly `len * 32` bytes, matching the output layout.
237        unsafe {
238            core::ptr::copy_nonoverlapping(slice.as_ptr(), out.as_mut_ptr().cast::<u8>(), byte_len);
239            Ok(core::slice::from_raw_parts_mut(out.as_mut_ptr().cast::<Self>(), len))
240        }
241    }
242
243    #[inline]
244    fn head_words(&self) -> usize {
245        1
246    }
247
248    #[inline]
249    fn tail_words(&self) -> usize {
250        0
251    }
252
253    #[inline]
254    fn head_append(&self, enc: &mut Encoder) {
255        enc.append_word(self.0);
256    }
257
258    #[inline]
259    fn head_append_many(tokens: &[Self], enc: &mut Encoder) {
260        // SAFETY: `WordToken` is `#[repr(transparent)]` over `Word`.
261        let words = unsafe { &*(tokens as *const [Self] as *const [Word]) };
262        enc.append_words(words);
263    }
264
265    #[inline]
266    fn tail_append(&self, _enc: &mut Encoder) {}
267}
268
269impl WordToken {
270    /// Create a new word token from a word.
271    #[inline]
272    pub const fn new(array: [u8; 32]) -> Self {
273        Self(FixedBytes(array))
274    }
275
276    /// Returns a reference to the word as a slice.
277    #[inline]
278    pub const fn as_slice(&self) -> &[u8] {
279        &self.0.0
280    }
281}
282
283/// A Fixed Sequence - `T[N]`
284#[derive(Clone, Debug, PartialEq, Eq)]
285pub struct FixedSeqToken<T, const N: usize>(pub [T; N]);
286
287impl<T, const N: usize> TryFrom<Vec<T>> for FixedSeqToken<T, N> {
288    type Error = <[T; N] as TryFrom<Vec<T>>>::Error;
289
290    #[inline]
291    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
292        <[T; N]>::try_from(value).map(Self)
293    }
294}
295
296impl<T, const N: usize> From<[T; N]> for FixedSeqToken<T, N> {
297    #[inline]
298    fn from(value: [T; N]) -> Self {
299        Self(value)
300    }
301}
302
303impl<T, const N: usize> AsRef<[T; N]> for FixedSeqToken<T, N> {
304    #[inline]
305    fn as_ref(&self) -> &[T; N] {
306        &self.0
307    }
308}
309
310impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken<T, N> {
311    const DYNAMIC: bool = T::DYNAMIC;
312
313    #[inline]
314    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
315        if Self::DYNAMIC {
316            dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
317        } else {
318            Self::decode_sequence(dec)
319        }
320    }
321
322    #[inline]
323    fn head_words(&self) -> usize {
324        if Self::DYNAMIC {
325            // offset
326            1
327        } else {
328            // elements
329            self.0.iter().map(T::total_words).sum()
330        }
331    }
332
333    #[inline]
334    fn tail_words(&self) -> usize {
335        if Self::DYNAMIC {
336            // elements
337            self.0.iter().map(T::total_words).sum()
338        } else {
339            0
340        }
341    }
342
343    #[inline]
344    fn head_append(&self, enc: &mut Encoder) {
345        if Self::DYNAMIC {
346            enc.append_indirection();
347        } else {
348            T::head_append_many(&self.0, enc);
349        }
350    }
351
352    #[inline]
353    fn tail_append(&self, enc: &mut Encoder) {
354        if Self::DYNAMIC {
355            self.encode_sequence(enc);
356        }
357    }
358}
359
360impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken<T, N> {
361    #[inline]
362    fn encode_sequence(&self, enc: &mut Encoder) {
363        encode_sequence_impl(&self.0, enc);
364    }
365
366    #[inline]
367    fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
368        let mut arr = crate::impl_core::uninit_array::<T, N>();
369        // SAFETY: `arr` is valid writable memory for `N` elements.
370        // `decode_many_from` initializes all elements on success.
371        unsafe {
372            T::decode_many_from(dec, &mut arr)?;
373            Ok(Self(crate::impl_core::array_assume_init(arr)))
374        }
375    }
376}
377
378impl<T, const N: usize> FixedSeqToken<T, N> {
379    /// Take the backing array, consuming the token.
380    // https://github.com/rust-lang/rust-clippy/issues/4979
381    #[allow(clippy::missing_const_for_fn)]
382    #[inline]
383    pub fn into_array(self) -> [T; N] {
384        self.0
385    }
386
387    /// Returns a reference to the array.
388    #[inline]
389    pub const fn as_array(&self) -> &[T; N] {
390        &self.0
391    }
392
393    /// Returns a reference to the array as a slice.
394    #[inline]
395    pub const fn as_slice(&self) -> &[T] {
396        &self.0
397    }
398}
399
400/// A Dynamic Sequence - `T[]`
401#[derive(Clone, Debug, PartialEq, Eq)]
402pub struct DynSeqToken<T>(pub Vec<T>);
403
404impl<T> From<Vec<T>> for DynSeqToken<T> {
405    #[inline]
406    fn from(value: Vec<T>) -> Self {
407        Self(value)
408    }
409}
410
411impl<T> AsRef<[T]> for DynSeqToken<T> {
412    #[inline]
413    fn as_ref(&self) -> &[T] {
414        self.0.as_ref()
415    }
416}
417
418impl<'de, T: Token<'de>> Token<'de> for DynSeqToken<T> {
419    const DYNAMIC: bool = true;
420
421    #[inline]
422    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
423        let mut child = dec.take_indirection()?;
424        let len = child.take_offset()?;
425        // This appears to be an unclarity in the Solidity spec. The spec
426        // specifies that offsets are relative to the first word of
427        // `enc(X)`. But known-good test vectors are relative to the
428        // word AFTER the array size
429        let mut child = child.raw_child()?;
430        let mut tokens = vec_try_with_capacity(len)?;
431        // SAFETY: `spare_capacity_mut` returns valid writable memory.
432        // `decode_many_from` initializes all `len` elements on success.
433        unsafe {
434            T::decode_many_from(&mut child, &mut tokens.spare_capacity_mut()[..len])?;
435            tokens.set_len(len);
436        }
437        Ok(Self(tokens))
438    }
439
440    #[inline]
441    fn head_words(&self) -> usize {
442        // offset
443        1
444    }
445
446    #[inline]
447    fn tail_words(&self) -> usize {
448        // length + elements
449        1 + self.0.iter().map(T::total_words).sum::<usize>()
450    }
451
452    #[inline]
453    fn head_append(&self, enc: &mut Encoder) {
454        enc.append_indirection();
455    }
456
457    #[inline]
458    fn tail_append(&self, enc: &mut Encoder) {
459        enc.append_seq_len(self.0.len());
460        self.encode_sequence(enc);
461    }
462}
463
464impl<'de, T: Token<'de>> TokenSeq<'de> for DynSeqToken<T> {
465    #[inline]
466    fn encode_sequence(&self, enc: &mut Encoder) {
467        encode_sequence_impl(&self.0, enc);
468    }
469
470    #[inline]
471    fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
472        Self::decode_from(dec)
473    }
474}
475
476impl<T> DynSeqToken<T> {
477    /// Returns a reference to the backing slice.
478    #[inline]
479    pub fn as_slice(&self) -> &[T] {
480        &self.0
481    }
482}
483
484/// A Packed Sequence - `bytes` or `string`
485#[derive(Clone, Copy, PartialEq, Eq)]
486pub struct PackedSeqToken<'a>(pub &'a [u8]);
487
488impl fmt::Debug for PackedSeqToken<'_> {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        f.debug_tuple("PackedSeqToken").field(&hex::encode_prefixed(self.0)).finish()
491    }
492}
493
494impl<'a> From<&'a [u8]> for PackedSeqToken<'a> {
495    #[inline]
496    fn from(value: &'a [u8]) -> Self {
497        Self(value)
498    }
499}
500
501impl<'a> From<&'a Vec<u8>> for PackedSeqToken<'a> {
502    #[inline]
503    fn from(value: &'a Vec<u8>) -> Self {
504        Self(value.as_slice())
505    }
506}
507
508impl AsRef<[u8]> for PackedSeqToken<'_> {
509    #[inline]
510    fn as_ref(&self) -> &[u8] {
511        self.0
512    }
513}
514
515impl<'de: 'a, 'a> Token<'de> for PackedSeqToken<'a> {
516    const DYNAMIC: bool = true;
517
518    #[inline]
519    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
520        let mut child = dec.take_indirection()?;
521        let len = child.take_offset()?;
522        let bytes = child.peek_len(len)?;
523        Ok(PackedSeqToken(bytes))
524    }
525
526    #[inline]
527    fn head_words(&self) -> usize {
528        // offset
529        1
530    }
531
532    #[inline]
533    fn tail_words(&self) -> usize {
534        // length + words(data)
535        1 + crate::utils::words_for(self.0)
536    }
537
538    #[inline]
539    fn head_append(&self, enc: &mut Encoder) {
540        enc.append_indirection();
541    }
542
543    #[inline]
544    fn tail_append(&self, enc: &mut Encoder) {
545        enc.append_packed_seq(self.0);
546    }
547}
548
549impl PackedSeqToken<'_> {
550    /// Instantiate a new [`Vec`] by copying the underlying slice.
551    // https://github.com/rust-lang/rust-clippy/issues/4979
552    #[allow(clippy::missing_const_for_fn)]
553    #[inline]
554    pub fn into_vec(self) -> Vec<u8> {
555        self.0.to_vec()
556    }
557
558    /// Instantiate a new [`Bytes`] by copying the underlying slice.
559    pub fn into_bytes(self) -> Bytes {
560        Bytes::copy_from_slice(self.0)
561    }
562
563    /// Returns a reference to the slice.
564    #[inline]
565    pub const fn as_slice(&self) -> &[u8] {
566        self.0
567    }
568}
569
570macro_rules! tuple_impls {
571    ($count:literal $($ty:ident),+) => {
572        impl<'de, $($ty: Token<'de>,)+> Sealed for ($($ty,)+) {}
573
574        #[allow(non_snake_case)]
575        impl<'de, $($ty: Token<'de>,)+> Token<'de> for ($($ty,)+) {
576            const DYNAMIC: bool = $( <$ty as Token>::DYNAMIC )||+;
577
578            #[inline]
579            fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
580                // The first element in a dynamic tuple is an offset to the tuple's data;
581                // for a static tuples, the data begins right away
582                if Self::DYNAMIC {
583                    dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
584                } else {
585                    Self::decode_sequence(dec)
586                }
587            }
588
589            #[inline]
590            fn head_words(&self) -> usize {
591                if Self::DYNAMIC {
592                    // offset
593                    1
594                } else {
595                    // elements
596                    let ($($ty,)+) = self;
597                    0 $( + $ty.total_words() )+
598                }
599            }
600
601            #[inline]
602            fn tail_words(&self) -> usize {
603                if Self::DYNAMIC {
604                    // elements
605                    let ($($ty,)+) = self;
606                    0 $( + $ty.total_words() )+
607                } else {
608                    0
609                }
610            }
611
612            #[inline]
613            fn head_append(&self, enc: &mut Encoder) {
614                if Self::DYNAMIC {
615                    enc.append_indirection();
616                } else {
617                    let ($($ty,)+) = self;
618                    $(
619                        $ty.head_append(enc);
620                    )+
621                }
622            }
623
624            #[inline]
625            fn tail_append(&self, enc: &mut Encoder) {
626                if Self::DYNAMIC {
627                    self.encode_sequence(enc);
628                }
629            }
630        }
631
632        #[allow(non_snake_case)]
633        impl<'de, $($ty: Token<'de>,)+> TokenSeq<'de> for ($($ty,)+) {
634            const IS_TUPLE: bool = true;
635
636            fn encode_sequence(&self, enc: &mut Encoder) {
637                let ($($ty,)+) = self;
638                enc.push_offset(0 $( + $ty.head_words() )+);
639
640                $(
641                    $ty.head_append(enc);
642                    enc.bump_offset($ty.tail_words());
643                )+
644
645                $(
646                    $ty.tail_append(enc);
647                )+
648
649                enc.pop_offset();
650            }
651
652            #[inline]
653            fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
654                Ok(($(
655                    match <$ty as Token>::decode_from(dec) {
656                        Ok(t) => t,
657                        Err(e) => return Err(e),
658                    },
659                )+))
660            }
661        }
662    };
663}
664
665impl<'de> Token<'de> for () {
666    const DYNAMIC: bool = false;
667
668    #[inline]
669    fn decode_from(_dec: &mut Decoder<'de>) -> Result<Self> {
670        Ok(())
671    }
672
673    #[inline]
674    fn head_words(&self) -> usize {
675        0
676    }
677
678    #[inline]
679    fn tail_words(&self) -> usize {
680        0
681    }
682
683    #[inline]
684    fn head_append(&self, _enc: &mut Encoder) {}
685
686    #[inline]
687    fn tail_append(&self, _enc: &mut Encoder) {}
688}
689
690impl<'de> TokenSeq<'de> for () {
691    const IS_TUPLE: bool = true;
692
693    #[inline]
694    fn encode_sequence(&self, _enc: &mut Encoder) {}
695
696    #[inline]
697    fn decode_sequence(_dec: &mut Decoder<'de>) -> Result<Self> {
698        Ok(())
699    }
700}
701
702all_the_tuples!(tuple_impls);
703
704/// Shared implementation for [`TokenSeq::encode_sequence`] used by both
705/// [`FixedSeqToken`] and [`DynSeqToken`].
706fn encode_sequence_impl<'de, T: Token<'de>>(tokens: &[T], enc: &mut Encoder) {
707    if T::DYNAMIC {
708        enc.push_offset(tokens.iter().map(T::head_words).sum());
709
710        for inner in tokens {
711            inner.head_append(enc);
712            enc.bump_offset(inner.tail_words());
713        }
714        for inner in tokens {
715            inner.tail_append(enc);
716        }
717
718        enc.pop_offset();
719    } else {
720        T::head_append_many(tokens, enc);
721    }
722}
723
724/// Initializes each element of `out` by calling `f` for each slot.
725///
726/// On success, all elements in `out` are initialized and returned as `&mut [T]`.
727/// On failure or panic, already-initialized elements are dropped.
728#[inline]
729fn try_init_each<T, E, F>(out: &mut [MaybeUninit<T>], mut f: F) -> core::result::Result<&mut [T], E>
730where
731    F: FnMut() -> core::result::Result<T, E>,
732{
733    struct Guard<'a, T> {
734        buf: &'a mut [MaybeUninit<T>],
735        initialized: usize,
736    }
737    impl<T> Drop for Guard<'_, T> {
738        fn drop(&mut self) {
739            // SAFETY: the first `self.initialized` elements are guaranteed initialized.
740            unsafe {
741                let ptr = self.buf.as_mut_ptr().cast::<T>();
742                ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, self.initialized));
743            }
744        }
745    }
746
747    let mut guard = Guard { buf: out, initialized: 0 };
748    for x in guard.buf.iter_mut() {
749        x.write(f()?);
750        guard.initialized += 1;
751    }
752    let buf = guard.buf as *mut [MaybeUninit<T>] as *mut [T];
753    mem::forget(guard);
754    // SAFETY: all `len` elements are initialized.
755    Ok(unsafe { &mut *buf })
756}
757
758#[cfg(test)]
759mod tests {
760    use super::*;
761    use crate::{SolType, sol_data};
762    use alloy_primitives::B256;
763
764    macro_rules! assert_type_check {
765        ($sol:ty, $token:expr $(,)?) => {
766            assert!(<$sol>::type_check($token).is_ok())
767        };
768    }
769
770    macro_rules! assert_not_type_check {
771        ($sol:ty, $token:expr $(,)?) => {
772            assert!(<$sol>::type_check($token).is_err())
773        };
774    }
775
776    #[test]
777    fn test_type_check() {
778        assert_type_check!(
779            (sol_data::Uint<256>, sol_data::Bool),
780            &(WordToken(B256::default()), WordToken(B256::default())),
781        );
782
783        // TODO(tests): more like this where we test type check internal logic
784        assert_not_type_check!(sol_data::Uint<8>, &Word::repeat_byte(0x11).into());
785        assert_not_type_check!(sol_data::Bool, &B256::repeat_byte(0x11).into());
786        assert_not_type_check!(sol_data::FixedBytes<31>, &B256::repeat_byte(0x11).into());
787
788        assert_type_check!(
789            (sol_data::Uint<32>, sol_data::Bool),
790            &(WordToken(B256::default()), WordToken(B256::default())),
791        );
792
793        assert_type_check!(
794            sol_data::Array<sol_data::Bool>,
795            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
796        );
797
798        assert_type_check!(
799            sol_data::Array<sol_data::Bool>,
800            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
801        );
802        assert_type_check!(
803            sol_data::Array<sol_data::Address>,
804            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
805        );
806
807        assert_type_check!(
808            sol_data::FixedArray<sol_data::Bool, 2>,
809            &FixedSeqToken::<_, 2>([
810                WordToken(B256::default()),
811                WordToken(B256::default()),
812            ]),
813        );
814
815        assert_type_check!(
816            sol_data::FixedArray<sol_data::Address, 2>,
817            &FixedSeqToken::<_, 2>([
818                WordToken(B256::default()),
819                WordToken(B256::default()),
820            ]),
821        );
822    }
823}