Skip to main content

alloy_dyn_abi/dynamic/
value.rs

1use super::ty::as_tuple;
2use crate::{DynSolType, DynToken, Word};
3use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
4use alloy_primitives::{Address, Function, I256, U256};
5use alloy_sol_types::{abi::Encoder, utils::words_for_len};
6
7#[cfg(feature = "eip712")]
8macro_rules! as_fixed_seq {
9    ($tuple:tt) => {
10        Self::CustomStruct { tuple: $tuple, .. } | Self::FixedArray($tuple) | Self::Tuple($tuple)
11    };
12}
13#[cfg(not(feature = "eip712"))]
14macro_rules! as_fixed_seq {
15    ($tuple:tt) => {
16        Self::FixedArray($tuple) | Self::Tuple($tuple)
17    };
18}
19
20/// A dynamic Solidity value.
21///
22/// It is broadly similar to `serde_json::Value` in that it is an enum of
23/// possible types, and the user must inspect and disambiguate.
24///
25/// # Examples
26///
27/// Basic usage:
28///
29/// ```
30/// use alloy_dyn_abi::{DynSolType, DynSolValue};
31///
32/// let ty: DynSolType = "uint64".parse()?;
33/// let value: DynSolValue = 183u64.into();
34///
35/// let encoded: Vec<u8> = value.abi_encode();
36/// let decoded: DynSolValue = ty.abi_decode(&encoded)?;
37///
38/// assert_eq!(decoded, value);
39/// # Ok::<(), alloy_dyn_abi::Error>(())
40/// ```
41///
42/// Coerce a string using [`DynSolType`]:
43///
44/// ```
45/// use alloy_dyn_abi::{DynSolType, DynSolValue};
46/// use alloy_primitives::U256;
47///
48/// let ty: DynSolType = "(string, uint256)".parse()?;
49#[cfg_attr(feature = "std", doc = "let value = ty.coerce_str(\"(foo bar, 2.5 gwei)\")?;")]
50#[cfg_attr(not(feature = "std"), doc = "let value = ty.coerce_str(\"(foo bar, 2500000000)\")?;")]
51/// assert_eq!(
52///     value,
53///     DynSolValue::Tuple(vec![
54///         DynSolValue::String(String::from("foo bar")),
55///         DynSolValue::Uint(U256::from(2_500_000_000u64), 256)
56///     ]),
57/// );
58/// # Ok::<(), alloy_dyn_abi::Error>(())
59/// ```
60#[derive(Clone, Debug, PartialEq)]
61pub enum DynSolValue {
62    /// A boolean.
63    Bool(bool),
64    /// A signed integer. The second parameter is the number of bits, not bytes.
65    Int(I256, usize),
66    /// An unsigned integer. The second parameter is the number of bits, not bytes.
67    Uint(U256, usize),
68    /// A fixed-length byte array. The second parameter is the number of bytes.
69    FixedBytes(Word, usize),
70    /// An address.
71    Address(Address),
72    /// A function pointer.
73    Function(Function),
74
75    /// A dynamic-length byte array.
76    Bytes(Vec<u8>),
77    /// A string.
78    String(String),
79
80    /// A dynamically-sized array of values.
81    Array(Vec<Self>),
82    /// A fixed-size array of values.
83    FixedArray(Vec<Self>),
84    /// A tuple of values.
85    Tuple(Vec<Self>),
86
87    /// A named struct, treated as a tuple with a name parameter.
88    #[cfg(feature = "eip712")]
89    CustomStruct {
90        /// The name of the struct.
91        name: String,
92        /// The struct's prop names, in declaration order.
93        prop_names: Vec<String>,
94        /// The inner types.
95        tuple: Vec<Self>,
96    },
97}
98
99impl From<Address> for DynSolValue {
100    #[inline]
101    fn from(value: Address) -> Self {
102        Self::Address(value)
103    }
104}
105
106impl From<bool> for DynSolValue {
107    #[inline]
108    fn from(value: bool) -> Self {
109        Self::Bool(value)
110    }
111}
112
113impl From<Vec<u8>> for DynSolValue {
114    #[inline]
115    fn from(value: Vec<u8>) -> Self {
116        Self::Bytes(value)
117    }
118}
119
120impl From<Word> for DynSolValue {
121    #[inline]
122    fn from(value: Word) -> Self {
123        Self::FixedBytes(value, 32)
124    }
125}
126
127impl From<String> for DynSolValue {
128    #[inline]
129    fn from(value: String) -> Self {
130        Self::String(value)
131    }
132}
133
134impl From<Vec<Self>> for DynSolValue {
135    #[inline]
136    fn from(value: Vec<Self>) -> Self {
137        Self::Array(value)
138    }
139}
140
141impl<const N: usize> From<[Self; N]> for DynSolValue {
142    #[inline]
143    fn from(value: [Self; N]) -> Self {
144        Self::FixedArray(value.to_vec())
145    }
146}
147
148macro_rules! impl_from_int {
149    ($($t:ty),+) => {$(
150        impl From<$t> for DynSolValue {
151            #[inline]
152            fn from(value: $t) -> Self {
153                const BITS: usize = <$t>::BITS as usize;
154                const BYTES: usize = BITS / 8;
155                const _: () = assert!(BYTES <= 32);
156
157                let mut word = if value.is_negative() {
158                    alloy_primitives::B256::repeat_byte(0xff)
159                } else {
160                    alloy_primitives::B256::ZERO
161                };
162                word[32 - BYTES..].copy_from_slice(&value.to_be_bytes());
163
164                Self::Int(I256::from_be_bytes(word.0), BITS)
165            }
166        }
167    )+};
168}
169
170impl_from_int!(i8, i16, i32, i64, isize, i128);
171
172impl From<I256> for DynSolValue {
173    #[inline]
174    fn from(value: I256) -> Self {
175        Self::Int(value, 256)
176    }
177}
178
179macro_rules! impl_from_uint {
180    ($($t:ty),+) => {$(
181        impl From<$t> for DynSolValue {
182            #[inline]
183            fn from(value: $t) -> Self {
184                Self::Uint(U256::from(value), <$t>::BITS as usize)
185            }
186        }
187    )+};
188}
189
190impl_from_uint!(u8, u16, u32, u64, usize, u128);
191
192impl From<U256> for DynSolValue {
193    #[inline]
194    fn from(value: U256) -> Self {
195        Self::Uint(value, 256)
196    }
197}
198
199impl DynSolValue {
200    /// The Solidity type. This returns the Solidity type corresponding to this
201    /// value, if it is known. A type will not be known if the value contains
202    /// an empty sequence, e.g. `T[0]`.
203    pub fn as_type(&self) -> Option<DynSolType> {
204        let ty = match self {
205            Self::Address(_) => DynSolType::Address,
206            Self::Function(_) => DynSolType::Function,
207            Self::Bool(_) => DynSolType::Bool,
208            Self::Bytes(_) => DynSolType::Bytes,
209            Self::FixedBytes(_, size) => DynSolType::FixedBytes(*size),
210            Self::Int(_, size) => DynSolType::Int(*size),
211            Self::Uint(_, size) => DynSolType::Uint(*size),
212            Self::String(_) => DynSolType::String,
213            Self::Tuple(inner) => {
214                return inner
215                    .iter()
216                    .map(Self::as_type)
217                    .collect::<Option<Vec<_>>>()
218                    .map(DynSolType::Tuple);
219            }
220            Self::Array(inner) => DynSolType::Array(Box::new(Self::as_type(inner.first()?)?)),
221            Self::FixedArray(inner) => {
222                DynSolType::FixedArray(Box::new(Self::as_type(inner.first()?)?), inner.len())
223            }
224            #[cfg(feature = "eip712")]
225            Self::CustomStruct { name, prop_names, tuple } => DynSolType::CustomStruct {
226                name: name.clone(),
227                prop_names: prop_names.clone(),
228                tuple: tuple.iter().map(Self::as_type).collect::<Option<Vec<_>>>()?,
229            },
230        };
231        Some(ty)
232    }
233
234    #[inline]
235    #[allow(clippy::missing_const_for_fn)]
236    fn sol_type_name_simple(&self) -> Option<&'static str> {
237        match self {
238            Self::Address(_) => Some("address"),
239            Self::Function(_) => Some("function"),
240            Self::Bool(_) => Some("bool"),
241            Self::Bytes(_) => Some("bytes"),
242            Self::String(_) => Some("string"),
243            _ => None,
244        }
245    }
246
247    fn sol_type_name_raw(&self, out: &mut String) {
248        match self {
249            Self::Address(_)
250            | Self::Function(_)
251            | Self::Bool(_)
252            | Self::Bytes(_)
253            | Self::String(_) => {
254                // SAFETY: `sol_type_name_simple` returns `Some` for these types
255                out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() });
256            }
257
258            Self::FixedBytes(_, size) | Self::Int(_, size) | Self::Uint(_, size) => {
259                let prefix = match self {
260                    Self::FixedBytes(..) => "bytes",
261                    Self::Int(..) => "int",
262                    Self::Uint(..) => "uint",
263                    _ => unreachable!(),
264                };
265                out.push_str(prefix);
266                out.push_str(itoa::Buffer::new().format(*size));
267            }
268
269            Self::Array(values) | Self::FixedArray(values) => {
270                // SAFETY: checked in `sol_type_name_capacity`
271                debug_assert!(!values.is_empty());
272                unsafe { values.first().unwrap_unchecked() }.sol_type_name_raw(out);
273
274                out.push('[');
275                let format_len = match self {
276                    Self::Array(_) => false,
277                    Self::FixedArray(_) => true,
278                    _ => unreachable!(),
279                };
280                if format_len {
281                    out.push_str(itoa::Buffer::new().format(values.len()));
282                }
283                out.push(']');
284            }
285            as_tuple!(Self tuple) => {
286                out.push('(');
287                for (i, val) in tuple.iter().enumerate() {
288                    if i > 0 {
289                        out.push(',');
290                    }
291                    val.sol_type_name_raw(out);
292                }
293                if tuple.len() == 1 {
294                    out.push(',');
295                }
296                out.push(')');
297            }
298        }
299    }
300
301    /// Returns an estimate of the number of bytes needed to format this type.
302    /// Returns `None` if it cannot be formatted.
303    ///
304    /// See `DynSolType::sol_type_name_capacity` for more info.
305    fn sol_type_name_capacity(&self) -> Option<usize> {
306        match self {
307            Self::Bool(_)
308            | Self::Int(..)
309            | Self::Uint(..)
310            | Self::FixedBytes(..)
311            | Self::Address(_)
312            | Self::Function(_)
313            | Self::Bytes(_)
314            | Self::String(_) => Some(8),
315
316            Self::Array(t) | Self::FixedArray(t) => {
317                t.first().and_then(Self::sol_type_name_capacity).map(|x| x + 8)
318            }
319
320            as_tuple!(Self tuple) => {
321                tuple.iter().map(Self::sol_type_name_capacity).sum::<Option<usize>>().map(|x| x + 8)
322            }
323        }
324    }
325
326    /// The Solidity type name. This returns the Solidity type corresponding to
327    /// this value, if it is known. A type will not be known if the value
328    /// contains an empty sequence, e.g. `T[0]`.
329    pub fn sol_type_name(&self) -> Option<Cow<'static, str>> {
330        if let Some(s) = self.sol_type_name_simple() {
331            Some(Cow::Borrowed(s))
332        } else if let Some(capacity) = self.sol_type_name_capacity() {
333            let mut s = String::with_capacity(capacity);
334            self.sol_type_name_raw(&mut s);
335            Some(Cow::Owned(s))
336        } else {
337            None
338        }
339    }
340
341    /// Trust if this value is encoded as a single word. False otherwise.
342    #[inline]
343    pub const fn is_word(&self) -> bool {
344        matches!(
345            self,
346            Self::Bool(_)
347                | Self::Int(..)
348                | Self::Uint(..)
349                | Self::FixedBytes(..)
350                | Self::Address(_)
351        )
352    }
353
354    /// Fallible cast to a single word. Will succeed for any single-word type.
355    #[inline]
356    pub fn as_word(&self) -> Option<Word> {
357        match *self {
358            Self::Bool(b) => Some(Word::with_last_byte(b as u8)),
359            Self::Int(i, _) => Some(i.into()),
360            Self::Uint(u, _) => Some(u.into()),
361            Self::FixedBytes(w, _) => Some(w),
362            Self::Address(a) => Some(a.into_word()),
363            Self::Function(f) => Some(f.into_word()),
364            _ => None,
365        }
366    }
367
368    /// Fallible cast to the contents of a variant DynSolValue {.
369    #[inline]
370    pub const fn as_address(&self) -> Option<Address> {
371        match self {
372            Self::Address(a) => Some(*a),
373            _ => None,
374        }
375    }
376
377    /// Fallible cast to the contents of a variant.
378    #[inline]
379    pub const fn as_bool(&self) -> Option<bool> {
380        match self {
381            Self::Bool(b) => Some(*b),
382            _ => None,
383        }
384    }
385
386    /// Fallible cast to the contents of a variant.
387    #[inline]
388    pub fn as_bytes(&self) -> Option<&[u8]> {
389        match self {
390            Self::Bytes(b) => Some(b),
391            _ => None,
392        }
393    }
394
395    /// Fallible cast to the contents of a variant.
396    #[inline]
397    pub const fn as_fixed_bytes(&self) -> Option<(&[u8], usize)> {
398        match self {
399            Self::FixedBytes(w, size) => Some((w.as_slice(), *size)),
400            _ => None,
401        }
402    }
403
404    /// Fallible cast to the contents of a variant.
405    #[inline]
406    pub const fn as_int(&self) -> Option<(I256, usize)> {
407        match self {
408            Self::Int(w, size) => Some((*w, *size)),
409            _ => None,
410        }
411    }
412
413    /// Fallible cast to the contents of a variant.
414    #[inline]
415    pub const fn as_uint(&self) -> Option<(U256, usize)> {
416        match self {
417            Self::Uint(u, size) => Some((*u, *size)),
418            _ => None,
419        }
420    }
421
422    /// Fallible cast to the contents of a variant.
423    #[inline]
424    pub fn as_str(&self) -> Option<&str> {
425        match self {
426            Self::String(s) => Some(s),
427            _ => None,
428        }
429    }
430
431    /// Fallible cast to the contents of a variant.
432    #[inline]
433    pub fn as_tuple(&self) -> Option<&[Self]> {
434        match self {
435            Self::Tuple(t) => Some(t),
436            _ => None,
437        }
438    }
439
440    /// Fallible cast to the contents of a variant.
441    #[inline]
442    pub fn as_array(&self) -> Option<&[Self]> {
443        match self {
444            Self::Array(a) => Some(a),
445            _ => None,
446        }
447    }
448
449    /// Fallible cast to the contents of a variant.
450    #[inline]
451    pub fn as_fixed_array(&self) -> Option<&[Self]> {
452        match self {
453            Self::FixedArray(a) => Some(a),
454            _ => None,
455        }
456    }
457
458    /// Fallible cast to the contents of a variant.
459    #[inline]
460    #[allow(clippy::missing_const_for_fn)]
461    pub fn as_custom_struct(&self) -> Option<(&str, &[String], &[Self])> {
462        match self {
463            #[cfg(feature = "eip712")]
464            Self::CustomStruct { name, prop_names, tuple } => Some((name, prop_names, tuple)),
465            _ => None,
466        }
467    }
468
469    /// Returns whether this type is contains a custom struct.
470    #[inline]
471    #[allow(clippy::missing_const_for_fn)]
472    pub fn has_custom_struct(&self) -> bool {
473        #[cfg(feature = "eip712")]
474        {
475            match self {
476                Self::CustomStruct { .. } => true,
477                Self::Array(t) | Self::FixedArray(t) | Self::Tuple(t) => {
478                    t.iter().any(Self::has_custom_struct)
479                }
480                _ => false,
481            }
482        }
483        #[cfg(not(feature = "eip712"))]
484        {
485            false
486        }
487    }
488
489    /// Returns true if the value is a sequence type.
490    #[inline]
491    #[allow(clippy::unneeded_wildcard_pattern)]
492    pub const fn is_sequence(&self) -> bool {
493        matches!(self, as_fixed_seq!(_) | Self::Array(_))
494    }
495
496    /// Fallible cast to a fixed-size array. Any of a `FixedArray`, a `Tuple`,
497    /// or a `CustomStruct`.
498    #[inline]
499    pub fn as_fixed_seq(&self) -> Option<&[Self]> {
500        match self {
501            as_fixed_seq!(tuple) => Some(tuple),
502            _ => None,
503        }
504    }
505
506    /// Fallible conversion to a sequence.
507    #[inline]
508    #[allow(clippy::missing_const_for_fn)] // erroneous lint
509    pub(crate) fn into_fixed_seq(self) -> Option<Vec<Self>> {
510        match self {
511            as_fixed_seq!(tuple) => Some(tuple),
512            _ => None,
513        }
514    }
515
516    /// Fallible cast to a packed sequence. Any of a String, or a Bytes.
517    #[inline]
518    pub fn as_packed_seq(&self) -> Option<&[u8]> {
519        match self {
520            Self::String(s) => Some(s.as_bytes()),
521            Self::Bytes(b) => Some(b),
522            _ => None,
523        }
524    }
525
526    /// Returns `true` if the value is an instance of a dynamically sized type.
527    #[inline]
528    pub fn is_dynamic(&self) -> bool {
529        match self {
530            Self::Address(_)
531            | Self::Function(_)
532            | Self::Bool(_)
533            | Self::Int(..)
534            | Self::Uint(..)
535            | Self::FixedBytes(..) => false,
536            Self::Bytes(_) | Self::String(_) | Self::Array(_) => true,
537            as_fixed_seq!(tuple) => tuple.iter().any(Self::is_dynamic),
538        }
539    }
540
541    /// Check that these values have the same type as the given [`DynSolType`]s.
542    ///
543    /// See [`DynSolType::matches`] for more information.
544    #[doc(alias = "types_check")] // from ethabi
545    #[inline(always)]
546    pub fn matches_many(values: &[Self], types: &[DynSolType]) -> bool {
547        DynSolType::matches_many(types, values)
548    }
549
550    /// Check that this value has the same type as the given [`DynSolType`].
551    ///
552    /// See [`DynSolType::matches`] for more information.
553    #[doc(alias = "type_check")] // from ethabi
554    #[inline(always)]
555    pub fn matches(&self, ty: &DynSolType) -> bool {
556        ty.matches(self)
557    }
558
559    /// Returns the number of words this type uses in the head of the ABI blob.
560    #[inline]
561    pub(crate) fn head_words(&self) -> usize {
562        match self.as_fixed_seq() {
563            // If dynamic 1 for the length, otherwise the sum of all head words.
564            Some(vals) => {
565                // `is_dynamic` iterates over all elements, and we need to sum all elements'
566                // head words, so do both things at once
567                let mut sum = 0;
568                for val in vals {
569                    if val.is_dynamic() {
570                        return 1;
571                    }
572                    sum += val.head_words();
573                }
574                sum
575            }
576            // Just a single word
577            None => 1,
578        }
579    }
580
581    /// Returns the number of words this type uses in the tail of the ABI blob.
582    #[inline]
583    pub(crate) fn tail_words(&self) -> usize {
584        match self {
585            // `self.is_word()`
586            Self::Address(_)
587            | Self::Function(_)
588            | Self::Bool(_)
589            | Self::FixedBytes(..)
590            | Self::Int(..)
591            | Self::Uint(..) => 0,
592
593            // `self.as_packed_seq()`
594            // 1 for the length, then the body padded to the next word.
595            Self::String(s) => 1 + words_for_len(s.len()),
596            Self::Bytes(b) => 1 + words_for_len(b.len()),
597
598            // `self.as_fixed_seq()`
599            // if static, 0.
600            // If dynamic, all words for all elements.
601            as_fixed_seq!(tuple) => {
602                // `is_dynamic` iterates over all elements, and we need to sum all elements'
603                // total words, so do both things at once
604                let mut any_dynamic = false;
605                let mut sum = 0;
606                for val in tuple {
607                    any_dynamic = any_dynamic || val.is_dynamic();
608                    sum += val.total_words();
609                }
610                any_dynamic as usize * sum
611            }
612
613            // `self.as_array()`
614            // 1 for the length. Then all words for all elements.
615            Self::Array(vals) => 1 + vals.iter().map(Self::total_words).sum::<usize>(),
616        }
617    }
618
619    /// Returns the total number of words this type uses in the ABI blob,
620    /// assuming it is not the top-level
621    #[inline]
622    pub(crate) fn total_words(&self) -> usize {
623        self.head_words() + self.tail_words()
624    }
625
626    /// Append this data to the head of an in-progress blob via the encoder.
627    #[inline]
628    pub fn head_append(&self, enc: &mut Encoder) {
629        match self {
630            Self::Address(_)
631            | Self::Function(_)
632            | Self::Bool(_)
633            | Self::FixedBytes(..)
634            | Self::Int(..)
635            | Self::Uint(..) => enc.append_word(unsafe { self.as_word().unwrap_unchecked() }),
636
637            Self::String(_) | Self::Bytes(_) | Self::Array(_) => enc.append_indirection(),
638
639            as_fixed_seq!(s) => {
640                if s.iter().any(Self::is_dynamic) {
641                    enc.append_indirection();
642                } else {
643                    for inner in s {
644                        inner.head_append(enc);
645                    }
646                }
647            }
648        }
649    }
650
651    /// Append this data to the tail of an in-progress blob via the encoder.
652    #[inline]
653    pub fn tail_append(&self, enc: &mut Encoder) {
654        match self {
655            Self::Address(_)
656            | Self::Function(_)
657            | Self::Bool(_)
658            | Self::FixedBytes(..)
659            | Self::Int(..)
660            | Self::Uint(..) => {}
661
662            Self::String(string) => enc.append_packed_seq(string.as_bytes()),
663            Self::Bytes(bytes) => enc.append_packed_seq(bytes),
664
665            as_fixed_seq!(s) => {
666                if self.is_dynamic() {
667                    Self::encode_seq_to(s, enc);
668                }
669            }
670
671            Self::Array(array) => {
672                enc.append_seq_len(array.len());
673                Self::encode_seq_to(array, enc);
674            }
675        }
676    }
677
678    /// Non-standard Packed Mode ABI encoding.
679    ///
680    /// Note that invalid value sizes will saturate to the maximum size, e.g. `Uint(x, 300)` will
681    /// behave the same as `Uint(x, 256)`.
682    ///
683    /// See [`SolType::abi_encode_packed`](alloy_sol_types::SolType::abi_encode_packed) for more
684    /// details.
685    #[inline]
686    pub fn abi_encode_packed(&self) -> Vec<u8> {
687        let mut buf = Vec::with_capacity(self.abi_packed_encoded_size());
688        self.abi_encode_packed_to(&mut buf);
689        buf
690    }
691
692    /// Non-standard Packed Mode ABI encoding.
693    ///
694    /// See [`abi_encode_packed`](Self::abi_encode_packed) for more details.
695    pub fn abi_encode_packed_to(&self, buf: &mut Vec<u8>) {
696        match self {
697            Self::Address(addr) => buf.extend_from_slice(addr.as_slice()),
698            Self::Function(func) => buf.extend_from_slice(func.as_slice()),
699            Self::Bool(b) => buf.push(*b as u8),
700            Self::String(s) => buf.extend_from_slice(s.as_bytes()),
701            Self::Bytes(bytes) => buf.extend_from_slice(bytes),
702            Self::FixedBytes(word, size) => buf.extend_from_slice(&word[..(*size).min(32)]),
703            Self::Int(num, size) => {
704                let byte_size = *size / 8;
705                let start = 32usize.saturating_sub(byte_size);
706                buf.extend_from_slice(&num.to_be_bytes::<32>()[start..]);
707            }
708            Self::Uint(num, size) => {
709                let byte_size = *size / 8;
710                let start = 32usize.saturating_sub(byte_size);
711                buf.extend_from_slice(&num.to_be_bytes::<32>()[start..]);
712            }
713            Self::FixedArray(inner) | Self::Array(inner) => {
714                for val in inner {
715                    // Array elements are left-padded to 32 bytes.
716                    if let Some(padding_needed) = 32usize.checked_sub(val.abi_packed_encoded_size())
717                    {
718                        buf.extend(core::iter::repeat_n(0, padding_needed));
719                    }
720                    val.abi_encode_packed_to(buf);
721                }
722            }
723            as_tuple!(Self inner) => {
724                for val in inner {
725                    val.abi_encode_packed_to(buf);
726                }
727            }
728        }
729    }
730
731    /// Returns the length of this value when ABI-encoded in Non-standard Packed Mode.
732    ///
733    /// See [`abi_encode_packed`](Self::abi_encode_packed) for more details.
734    pub fn abi_packed_encoded_size(&self) -> usize {
735        match self {
736            Self::Address(_) | Self::Function(_) => 20,
737            Self::Bool(_) => 1,
738            Self::String(s) => s.len(),
739            Self::Bytes(b) => b.len(),
740            Self::FixedBytes(_, size) => (*size).min(32),
741            Self::Int(_, size) | Self::Uint(_, size) => (size / 8).min(32),
742            Self::FixedArray(inner) | Self::Array(inner) => {
743                inner.iter().map(|v| v.abi_packed_encoded_size().max(32)).sum()
744            }
745            as_tuple!(Self inner) => inner.iter().map(Self::abi_packed_encoded_size).sum(),
746        }
747    }
748
749    /// Tokenize this value into a [`DynToken`].
750    pub fn tokenize(&self) -> DynToken<'_> {
751        match self {
752            Self::Address(a) => a.into_word().into(),
753            Self::Function(f) => f.into_word().into(),
754            Self::Bool(b) => Word::with_last_byte(*b as u8).into(),
755            Self::Bytes(buf) => DynToken::PackedSeq(buf),
756            Self::FixedBytes(buf, _) => (*buf).into(),
757            Self::Int(int, _) => int.to_be_bytes::<32>().into(),
758            Self::Uint(uint, _) => uint.to_be_bytes::<32>().into(),
759            Self::String(s) => DynToken::PackedSeq(s.as_bytes()),
760            Self::Array(t) => DynToken::from_dyn_seq(t),
761            as_fixed_seq!(t) => DynToken::from_fixed_seq(t),
762        }
763    }
764
765    /// Encode this data as a sequence.
766    pub(crate) fn encode_seq(seq: &[Self]) -> Vec<u8> {
767        let sz = seq.iter().map(Self::total_words).sum();
768        let mut encoder = Encoder::with_capacity(sz);
769        Self::encode_seq_to(seq, &mut encoder);
770        encoder.into_bytes()
771    }
772
773    /// Encode this data as a sequence into the given encoder.
774    pub(crate) fn encode_seq_to(contents: &[Self], enc: &mut Encoder) {
775        let head_words = contents.iter().map(Self::head_words).sum::<usize>();
776        enc.push_offset(head_words);
777
778        for t in contents {
779            t.head_append(enc);
780            enc.bump_offset(t.tail_words());
781        }
782
783        for t in contents {
784            t.tail_append(enc);
785        }
786
787        enc.pop_offset();
788    }
789
790    /// Encode this value into a byte array by wrapping it into a 1-element
791    /// sequence.
792    #[inline]
793    pub fn abi_encode(&self) -> Vec<u8> {
794        Self::encode_seq(core::slice::from_ref(self))
795    }
796
797    /// Encode this value into a byte array suitable for passing to a function.
798    /// If this value is a tuple, it is encoded as is. Otherwise, it is wrapped
799    /// into a 1-element sequence.
800    ///
801    /// # Examples
802    ///
803    /// ```ignore (pseudo-code)
804    /// // Encoding for function foo(address)
805    /// DynSolValue::Address(_).abi_encode_params();
806    ///
807    /// // Encoding for function foo(address, uint256)
808    /// DynSolValue::Tuple(vec![
809    ///     DynSolValue::Address(_),
810    ///     DynSolValue::Uint(_, 256),
811    /// ]).abi_encode_params();
812    /// ```
813    #[inline]
814    pub fn abi_encode_params(&self) -> Vec<u8> {
815        match self {
816            Self::Tuple(seq) => Self::encode_seq(seq),
817            _ => self.abi_encode(),
818        }
819    }
820
821    /// If this value is a fixed sequence, encode it into a byte array. If this
822    /// value is not a fixed sequence, return `None`.
823    #[inline]
824    pub fn abi_encode_sequence(&self) -> Option<Vec<u8>> {
825        self.as_fixed_seq().map(Self::encode_seq)
826    }
827}