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    abi::{Decoder, Encoder},
16    Result, Word,
17};
18use alloc::vec::Vec;
19use alloy_primitives::{utils::vec_try_with_capacity, Bytes, FixedBytes, I256, U256};
20use core::fmt;
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    /// Calculate the number of head words.
62    fn head_words(&self) -> usize;
63
64    /// Calculate the number of tail words.
65    fn tail_words(&self) -> usize;
66
67    /// Calculate the total number of head and tail words.
68    #[inline]
69    fn total_words(&self) -> usize {
70        self.head_words() + self.tail_words()
71    }
72
73    /// Append head words to the encoder.
74    fn head_append(&self, enc: &mut Encoder);
75
76    /// Append tail words to the encoder.
77    fn tail_append(&self, enc: &mut Encoder);
78}
79
80/// A token composed of a sequence of other tokens.
81///
82/// This functions is an extension trait for [`Token`], and is only
83/// implemented by [`FixedSeqToken`], [`DynSeqToken`], [`PackedSeqToken`], and
84/// tuples of [`Token`]s (including [`WordToken`]).
85pub trait TokenSeq<'a>: Token<'a> {
86    /// True for tuples only.
87    const IS_TUPLE: bool = false;
88
89    /// ABI-encode the token sequence into the encoder.
90    fn encode_sequence(&self, enc: &mut Encoder);
91
92    /// ABI-decode the token sequence from the encoder.
93    fn decode_sequence(dec: &mut Decoder<'a>) -> Result<Self>;
94}
95
96/// A single EVM word - T for any value type.
97#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
98pub struct WordToken(pub Word);
99
100impl<T> From<&T> for WordToken
101where
102    T: Clone,
103    Self: From<T>,
104{
105    #[inline]
106    fn from(value: &T) -> Self {
107        Self::from(value.clone())
108    }
109}
110
111impl<T> From<&mut T> for WordToken
112where
113    T: Clone,
114    Self: From<T>,
115{
116    #[inline]
117    fn from(value: &mut T) -> Self {
118        Self::from(value.clone())
119    }
120}
121
122impl From<Word> for WordToken {
123    #[inline]
124    fn from(value: Word) -> Self {
125        Self(value)
126    }
127}
128
129impl From<WordToken> for Word {
130    #[inline]
131    fn from(value: WordToken) -> Self {
132        value.0
133    }
134}
135
136impl From<bool> for WordToken {
137    #[inline]
138    fn from(value: bool) -> Self {
139        U256::from(value as u64).into()
140    }
141}
142
143impl From<U256> for WordToken {
144    #[inline]
145    fn from(value: U256) -> Self {
146        Self(value.into())
147    }
148}
149
150impl From<I256> for WordToken {
151    #[inline]
152    fn from(value: I256) -> Self {
153        Self(value.into())
154    }
155}
156
157impl From<WordToken> for [u8; 32] {
158    #[inline]
159    fn from(value: WordToken) -> [u8; 32] {
160        value.0.into()
161    }
162}
163
164impl From<[u8; 32]> for WordToken {
165    #[inline]
166    fn from(value: [u8; 32]) -> Self {
167        Self(value.into())
168    }
169}
170
171impl AsRef<Word> for WordToken {
172    #[inline]
173    fn as_ref(&self) -> &Word {
174        &self.0
175    }
176}
177
178impl AsRef<[u8]> for WordToken {
179    #[inline]
180    fn as_ref(&self) -> &[u8] {
181        &self.0 .0
182    }
183}
184
185impl<'a> Token<'a> for WordToken {
186    const DYNAMIC: bool = false;
187
188    #[inline]
189    fn decode_from(dec: &mut Decoder<'a>) -> Result<Self> {
190        dec.take_word().copied().map(Self)
191    }
192
193    #[inline]
194    fn head_words(&self) -> usize {
195        1
196    }
197
198    #[inline]
199    fn tail_words(&self) -> usize {
200        0
201    }
202
203    #[inline]
204    fn head_append(&self, enc: &mut Encoder) {
205        enc.append_word(self.0);
206    }
207
208    #[inline]
209    fn tail_append(&self, _enc: &mut Encoder) {}
210}
211
212impl WordToken {
213    /// Create a new word token from a word.
214    #[inline]
215    pub const fn new(array: [u8; 32]) -> Self {
216        Self(FixedBytes(array))
217    }
218
219    /// Returns a reference to the word as a slice.
220    #[inline]
221    pub const fn as_slice(&self) -> &[u8] {
222        &self.0 .0
223    }
224}
225
226/// A Fixed Sequence - `T[N]`
227#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct FixedSeqToken<T, const N: usize>(pub [T; N]);
229
230impl<T, const N: usize> TryFrom<Vec<T>> for FixedSeqToken<T, N> {
231    type Error = <[T; N] as TryFrom<Vec<T>>>::Error;
232
233    #[inline]
234    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
235        <[T; N]>::try_from(value).map(Self)
236    }
237}
238
239impl<T, const N: usize> From<[T; N]> for FixedSeqToken<T, N> {
240    #[inline]
241    fn from(value: [T; N]) -> Self {
242        Self(value)
243    }
244}
245
246impl<T, const N: usize> AsRef<[T; N]> for FixedSeqToken<T, N> {
247    #[inline]
248    fn as_ref(&self) -> &[T; N] {
249        &self.0
250    }
251}
252
253impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken<T, N> {
254    const DYNAMIC: bool = T::DYNAMIC;
255
256    #[inline]
257    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
258        if Self::DYNAMIC {
259            dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
260        } else {
261            Self::decode_sequence(dec)
262        }
263    }
264
265    #[inline]
266    fn head_words(&self) -> usize {
267        if Self::DYNAMIC {
268            // offset
269            1
270        } else {
271            // elements
272            self.0.iter().map(T::total_words).sum()
273        }
274    }
275
276    #[inline]
277    fn tail_words(&self) -> usize {
278        if Self::DYNAMIC {
279            // elements
280            self.0.iter().map(T::total_words).sum()
281        } else {
282            0
283        }
284    }
285
286    #[inline]
287    fn head_append(&self, enc: &mut Encoder) {
288        if Self::DYNAMIC {
289            enc.append_indirection();
290        } else {
291            for inner in &self.0 {
292                inner.head_append(enc);
293            }
294        }
295    }
296
297    #[inline]
298    fn tail_append(&self, enc: &mut Encoder) {
299        if Self::DYNAMIC {
300            self.encode_sequence(enc);
301        }
302    }
303}
304
305impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken<T, N> {
306    fn encode_sequence(&self, enc: &mut Encoder) {
307        enc.push_offset(self.0.iter().map(T::head_words).sum());
308
309        for inner in &self.0 {
310            inner.head_append(enc);
311            enc.bump_offset(inner.tail_words());
312        }
313        for inner in &self.0 {
314            inner.tail_append(enc);
315        }
316
317        enc.pop_offset();
318    }
319
320    #[inline]
321    fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
322        crate::impl_core::try_from_fn(|_| T::decode_from(dec)).map(Self)
323    }
324}
325
326impl<T, const N: usize> FixedSeqToken<T, N> {
327    /// Take the backing array, consuming the token.
328    // https://github.com/rust-lang/rust-clippy/issues/4979
329    #[allow(clippy::missing_const_for_fn)]
330    #[inline]
331    pub fn into_array(self) -> [T; N] {
332        self.0
333    }
334
335    /// Returns a reference to the array.
336    #[inline]
337    pub const fn as_array(&self) -> &[T; N] {
338        &self.0
339    }
340
341    /// Returns a reference to the array as a slice.
342    #[inline]
343    pub const fn as_slice(&self) -> &[T] {
344        &self.0
345    }
346}
347
348/// A Dynamic Sequence - `T[]`
349#[derive(Clone, Debug, PartialEq, Eq)]
350pub struct DynSeqToken<T>(pub Vec<T>);
351
352impl<T> From<Vec<T>> for DynSeqToken<T> {
353    #[inline]
354    fn from(value: Vec<T>) -> Self {
355        Self(value)
356    }
357}
358
359impl<T> AsRef<[T]> for DynSeqToken<T> {
360    #[inline]
361    fn as_ref(&self) -> &[T] {
362        self.0.as_ref()
363    }
364}
365
366impl<'de, T: Token<'de>> Token<'de> for DynSeqToken<T> {
367    const DYNAMIC: bool = true;
368
369    #[inline]
370    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
371        let mut child = dec.take_indirection()?;
372        let len = child.take_offset()?;
373        // This appears to be an unclarity in the Solidity spec. The spec
374        // specifies that offsets are relative to the first word of
375        // `enc(X)`. But known-good test vectors are relative to the
376        // word AFTER the array size
377        let mut child = child.raw_child()?;
378        let mut tokens = vec_try_with_capacity(len)?;
379        for _ in 0..len {
380            tokens.push(T::decode_from(&mut child)?);
381        }
382        Ok(Self(tokens))
383    }
384
385    #[inline]
386    fn head_words(&self) -> usize {
387        // offset
388        1
389    }
390
391    #[inline]
392    fn tail_words(&self) -> usize {
393        // length + elements
394        1 + self.0.iter().map(T::total_words).sum::<usize>()
395    }
396
397    #[inline]
398    fn head_append(&self, enc: &mut Encoder) {
399        enc.append_indirection();
400    }
401
402    #[inline]
403    fn tail_append(&self, enc: &mut Encoder) {
404        enc.append_seq_len(self.0.len());
405        self.encode_sequence(enc);
406    }
407}
408
409impl<'de, T: Token<'de>> TokenSeq<'de> for DynSeqToken<T> {
410    fn encode_sequence(&self, enc: &mut Encoder) {
411        enc.push_offset(self.0.iter().map(T::head_words).sum());
412
413        for inner in &self.0 {
414            inner.head_append(enc);
415            enc.bump_offset(inner.tail_words());
416        }
417        for inner in &self.0 {
418            inner.tail_append(enc);
419        }
420
421        enc.pop_offset();
422    }
423
424    #[inline]
425    fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
426        Self::decode_from(dec)
427    }
428}
429
430impl<T> DynSeqToken<T> {
431    /// Returns a reference to the backing slice.
432    #[inline]
433    pub fn as_slice(&self) -> &[T] {
434        &self.0
435    }
436}
437
438/// A Packed Sequence - `bytes` or `string`
439#[derive(Clone, Copy, PartialEq, Eq)]
440pub struct PackedSeqToken<'a>(pub &'a [u8]);
441
442impl fmt::Debug for PackedSeqToken<'_> {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        f.debug_tuple("PackedSeqToken").field(&hex::encode_prefixed(self.0)).finish()
445    }
446}
447
448impl<'a> From<&'a [u8]> for PackedSeqToken<'a> {
449    fn from(value: &'a [u8]) -> Self {
450        Self(value)
451    }
452}
453
454impl<'a> From<&'a Vec<u8>> for PackedSeqToken<'a> {
455    fn from(value: &'a Vec<u8>) -> Self {
456        Self(value.as_slice())
457    }
458}
459
460impl AsRef<[u8]> for PackedSeqToken<'_> {
461    fn as_ref(&self) -> &[u8] {
462        self.0
463    }
464}
465
466impl<'de: 'a, 'a> Token<'de> for PackedSeqToken<'a> {
467    const DYNAMIC: bool = true;
468
469    #[inline]
470    fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
471        let mut child = dec.take_indirection()?;
472        let len = child.take_offset()?;
473        let bytes = child.peek_len(len)?;
474        Ok(PackedSeqToken(bytes))
475    }
476
477    #[inline]
478    fn head_words(&self) -> usize {
479        // offset
480        1
481    }
482
483    #[inline]
484    fn tail_words(&self) -> usize {
485        // length + words(data)
486        1 + crate::utils::words_for(self.0)
487    }
488
489    #[inline]
490    fn head_append(&self, enc: &mut Encoder) {
491        enc.append_indirection();
492    }
493
494    #[inline]
495    fn tail_append(&self, enc: &mut Encoder) {
496        enc.append_packed_seq(self.0);
497    }
498}
499
500impl PackedSeqToken<'_> {
501    /// Instantiate a new [`Vec`] by copying the underlying slice.
502    // https://github.com/rust-lang/rust-clippy/issues/4979
503    #[allow(clippy::missing_const_for_fn)]
504    #[inline]
505    pub fn into_vec(self) -> Vec<u8> {
506        self.0.to_vec()
507    }
508
509    /// Instantiate a new [`Bytes`] by copying the underlying slice.
510    pub fn into_bytes(self) -> Bytes {
511        Bytes::copy_from_slice(self.0)
512    }
513
514    /// Returns a reference to the slice.
515    #[inline]
516    pub const fn as_slice(&self) -> &[u8] {
517        self.0
518    }
519}
520
521macro_rules! tuple_impls {
522    ($count:literal $($ty:ident),+) => {
523        impl<'de, $($ty: Token<'de>,)+> Sealed for ($($ty,)+) {}
524
525        #[allow(non_snake_case)]
526        impl<'de, $($ty: Token<'de>,)+> Token<'de> for ($($ty,)+) {
527            const DYNAMIC: bool = $( <$ty as Token>::DYNAMIC )||+;
528
529            #[inline]
530            fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
531                // The first element in a dynamic tuple is an offset to the tuple's data;
532                // for a static tuples, the data begins right away
533                if Self::DYNAMIC {
534                    dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
535                } else {
536                    Self::decode_sequence(dec)
537                }
538            }
539
540            #[inline]
541            fn head_words(&self) -> usize {
542                if Self::DYNAMIC {
543                    // offset
544                    1
545                } else {
546                    // elements
547                    let ($($ty,)+) = self;
548                    0 $( + $ty.total_words() )+
549                }
550            }
551
552            #[inline]
553            fn tail_words(&self) -> usize {
554                if Self::DYNAMIC {
555                    // elements
556                    let ($($ty,)+) = self;
557                    0 $( + $ty.total_words() )+
558                } else {
559                    0
560                }
561            }
562
563            #[inline]
564            fn head_append(&self, enc: &mut Encoder) {
565                if Self::DYNAMIC {
566                    enc.append_indirection();
567                } else {
568                    let ($($ty,)+) = self;
569                    $(
570                        $ty.head_append(enc);
571                    )+
572                }
573            }
574
575            #[inline]
576            fn tail_append(&self, enc: &mut Encoder) {
577                if Self::DYNAMIC {
578                    self.encode_sequence(enc);
579                }
580            }
581        }
582
583        #[allow(non_snake_case)]
584        impl<'de, $($ty: Token<'de>,)+> TokenSeq<'de> for ($($ty,)+) {
585            const IS_TUPLE: bool = true;
586
587            fn encode_sequence(&self, enc: &mut Encoder) {
588                let ($($ty,)+) = self;
589                enc.push_offset(0 $( + $ty.head_words() )+);
590
591                $(
592                    $ty.head_append(enc);
593                    enc.bump_offset($ty.tail_words());
594                )+
595
596                $(
597                    $ty.tail_append(enc);
598                )+
599
600                enc.pop_offset();
601            }
602
603            #[inline]
604            fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
605                Ok(($(
606                    match <$ty as Token>::decode_from(dec) {
607                        Ok(t) => t,
608                        Err(e) => return Err(e),
609                    },
610                )+))
611            }
612        }
613    };
614}
615
616impl<'de> Token<'de> for () {
617    const DYNAMIC: bool = false;
618
619    #[inline]
620    fn decode_from(_dec: &mut Decoder<'de>) -> Result<Self> {
621        Ok(())
622    }
623
624    #[inline]
625    fn head_words(&self) -> usize {
626        0
627    }
628
629    #[inline]
630    fn tail_words(&self) -> usize {
631        0
632    }
633
634    #[inline]
635    fn head_append(&self, _enc: &mut Encoder) {}
636
637    #[inline]
638    fn tail_append(&self, _enc: &mut Encoder) {}
639}
640
641impl<'de> TokenSeq<'de> for () {
642    const IS_TUPLE: bool = true;
643
644    #[inline]
645    fn encode_sequence(&self, _enc: &mut Encoder) {}
646
647    #[inline]
648    fn decode_sequence(_dec: &mut Decoder<'de>) -> Result<Self> {
649        Ok(())
650    }
651}
652
653all_the_tuples!(tuple_impls);
654
655#[cfg(test)]
656mod tests {
657    use super::*;
658    use crate::{sol_data, SolType};
659    use alloy_primitives::B256;
660
661    macro_rules! assert_type_check {
662        ($sol:ty, $token:expr $(,)?) => {
663            assert!(<$sol>::type_check($token).is_ok())
664        };
665    }
666
667    macro_rules! assert_not_type_check {
668        ($sol:ty, $token:expr $(,)?) => {
669            assert!(<$sol>::type_check($token).is_err())
670        };
671    }
672
673    #[test]
674    fn test_type_check() {
675        assert_type_check!(
676            (sol_data::Uint<256>, sol_data::Bool),
677            &(WordToken(B256::default()), WordToken(B256::default())),
678        );
679
680        // TODO(tests): more like this where we test type check internal logic
681        assert_not_type_check!(sol_data::Uint<8>, &Word::repeat_byte(0x11).into());
682        assert_not_type_check!(sol_data::Bool, &B256::repeat_byte(0x11).into());
683        assert_not_type_check!(sol_data::FixedBytes<31>, &B256::repeat_byte(0x11).into());
684
685        assert_type_check!(
686            (sol_data::Uint<32>, sol_data::Bool),
687            &(WordToken(B256::default()), WordToken(B256::default())),
688        );
689
690        assert_type_check!(
691            sol_data::Array<sol_data::Bool>,
692            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
693        );
694
695        assert_type_check!(
696            sol_data::Array<sol_data::Bool>,
697            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
698        );
699        assert_type_check!(
700            sol_data::Array<sol_data::Address>,
701            &DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
702        );
703
704        assert_type_check!(
705            sol_data::FixedArray<sol_data::Bool, 2>,
706            &FixedSeqToken::<_, 2>([
707                WordToken(B256::default()),
708                WordToken(B256::default()),
709            ]),
710        );
711
712        assert_type_check!(
713            sol_data::FixedArray<sol_data::Address, 2>,
714            &FixedSeqToken::<_, 2>([
715                WordToken(B256::default()),
716                WordToken(B256::default()),
717            ]),
718        );
719    }
720}