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    pub const fn is_sequence(&self) -> bool {
492        matches!(self, as_fixed_seq!(_) | Self::Array(_))
493    }
494
495    /// Fallible cast to a fixed-size array. Any of a `FixedArray`, a `Tuple`,
496    /// or a `CustomStruct`.
497    #[inline]
498    pub fn as_fixed_seq(&self) -> Option<&[Self]> {
499        match self {
500            as_fixed_seq!(tuple) => Some(tuple),
501            _ => None,
502        }
503    }
504
505    /// Fallible conversion to a sequence.
506    #[inline]
507    #[allow(clippy::missing_const_for_fn)] // erroneous lint
508    pub(crate) fn into_fixed_seq(self) -> Option<Vec<Self>> {
509        match self {
510            as_fixed_seq!(tuple) => Some(tuple),
511            _ => None,
512        }
513    }
514
515    /// Fallible cast to a packed sequence. Any of a String, or a Bytes.
516    #[inline]
517    pub fn as_packed_seq(&self) -> Option<&[u8]> {
518        match self {
519            Self::String(s) => Some(s.as_bytes()),
520            Self::Bytes(b) => Some(b),
521            _ => None,
522        }
523    }
524
525    /// Returns `true` if the value is an instance of a dynamically sized type.
526    #[inline]
527    pub fn is_dynamic(&self) -> bool {
528        match self {
529            Self::Address(_)
530            | Self::Function(_)
531            | Self::Bool(_)
532            | Self::Int(..)
533            | Self::Uint(..)
534            | Self::FixedBytes(..) => false,
535            Self::Bytes(_) | Self::String(_) | Self::Array(_) => true,
536            as_fixed_seq!(tuple) => tuple.iter().any(Self::is_dynamic),
537        }
538    }
539
540    /// Check that these values have the same type as the given [`DynSolType`]s.
541    ///
542    /// See [`DynSolType::matches`] for more information.
543    #[doc(alias = "types_check")] // from ethabi
544    #[inline(always)]
545    pub fn matches_many(values: &[Self], types: &[DynSolType]) -> bool {
546        DynSolType::matches_many(types, values)
547    }
548
549    /// Check that this value has the same type as the given [`DynSolType`].
550    ///
551    /// See [`DynSolType::matches`] for more information.
552    #[doc(alias = "type_check")] // from ethabi
553    #[inline(always)]
554    pub fn matches(&self, ty: &DynSolType) -> bool {
555        ty.matches(self)
556    }
557
558    /// Returns the number of words this type uses in the head of the ABI blob.
559    #[inline]
560    pub(crate) fn head_words(&self) -> usize {
561        match self.as_fixed_seq() {
562            // If dynamic 1 for the length, otherwise the sum of all head words.
563            Some(vals) => {
564                // `is_dynamic` iterates over all elements, and we need to sum all elements'
565                // head words, so do both things at once
566                let mut sum = 0;
567                for val in vals {
568                    if val.is_dynamic() {
569                        return 1;
570                    }
571                    sum += val.head_words();
572                }
573                sum
574            }
575            // Just a single word
576            None => 1,
577        }
578    }
579
580    /// Returns the number of words this type uses in the tail of the ABI blob.
581    #[inline]
582    pub(crate) fn tail_words(&self) -> usize {
583        match self {
584            // `self.is_word()`
585            Self::Address(_)
586            | Self::Function(_)
587            | Self::Bool(_)
588            | Self::FixedBytes(..)
589            | Self::Int(..)
590            | Self::Uint(..) => 0,
591
592            // `self.as_packed_seq()`
593            // 1 for the length, then the body padded to the next word.
594            Self::String(s) => 1 + words_for_len(s.len()),
595            Self::Bytes(b) => 1 + words_for_len(b.len()),
596
597            // `self.as_fixed_seq()`
598            // if static, 0.
599            // If dynamic, all words for all elements.
600            as_fixed_seq!(tuple) => {
601                // `is_dynamic` iterates over all elements, and we need to sum all elements'
602                // total words, so do both things at once
603                let mut any_dynamic = false;
604                let mut sum = 0;
605                for val in tuple {
606                    any_dynamic = any_dynamic || val.is_dynamic();
607                    sum += val.total_words();
608                }
609                any_dynamic as usize * sum
610            }
611
612            // `self.as_array()`
613            // 1 for the length. Then all words for all elements.
614            Self::Array(vals) => 1 + vals.iter().map(Self::total_words).sum::<usize>(),
615        }
616    }
617
618    /// Returns the total number of words this type uses in the ABI blob,
619    /// assuming it is not the top-level
620    #[inline]
621    pub(crate) fn total_words(&self) -> usize {
622        self.head_words() + self.tail_words()
623    }
624
625    /// Append this data to the head of an in-progress blob via the encoder.
626    #[inline]
627    pub fn head_append(&self, enc: &mut Encoder) {
628        match self {
629            Self::Address(_)
630            | Self::Function(_)
631            | Self::Bool(_)
632            | Self::FixedBytes(..)
633            | Self::Int(..)
634            | Self::Uint(..) => enc.append_word(unsafe { self.as_word().unwrap_unchecked() }),
635
636            Self::String(_) | Self::Bytes(_) | Self::Array(_) => enc.append_indirection(),
637
638            as_fixed_seq!(s) => {
639                if s.iter().any(Self::is_dynamic) {
640                    enc.append_indirection();
641                } else {
642                    for inner in s {
643                        inner.head_append(enc);
644                    }
645                }
646            }
647        }
648    }
649
650    /// Append this data to the tail of an in-progress blob via the encoder.
651    #[inline]
652    pub fn tail_append(&self, enc: &mut Encoder) {
653        match self {
654            Self::Address(_)
655            | Self::Function(_)
656            | Self::Bool(_)
657            | Self::FixedBytes(..)
658            | Self::Int(..)
659            | Self::Uint(..) => {}
660
661            Self::String(string) => enc.append_packed_seq(string.as_bytes()),
662            Self::Bytes(bytes) => enc.append_packed_seq(bytes),
663
664            as_fixed_seq!(s) => {
665                if self.is_dynamic() {
666                    Self::encode_seq_to(s, enc);
667                }
668            }
669
670            Self::Array(array) => {
671                enc.append_seq_len(array.len());
672                Self::encode_seq_to(array, enc);
673            }
674        }
675    }
676
677    /// Non-standard Packed Mode ABI encoding.
678    ///
679    /// Note that invalid value sizes will saturate to the maximum size, e.g. `Uint(x, 300)` will
680    /// behave the same as `Uint(x, 256)`.
681    ///
682    /// See [`SolType::abi_encode_packed`](alloy_sol_types::SolType::abi_encode_packed) for more
683    /// details.
684    #[inline]
685    pub fn abi_encode_packed(&self) -> Vec<u8> {
686        let mut buf = Vec::with_capacity(self.abi_packed_encoded_size());
687        self.abi_encode_packed_to(&mut buf);
688        buf
689    }
690
691    /// Non-standard Packed Mode ABI encoding.
692    ///
693    /// See [`abi_encode_packed`](Self::abi_encode_packed) for more details.
694    pub fn abi_encode_packed_to(&self, buf: &mut Vec<u8>) {
695        match self {
696            Self::Address(addr) => buf.extend_from_slice(addr.as_slice()),
697            Self::Function(func) => buf.extend_from_slice(func.as_slice()),
698            Self::Bool(b) => buf.push(*b as u8),
699            Self::String(s) => buf.extend_from_slice(s.as_bytes()),
700            Self::Bytes(bytes) => buf.extend_from_slice(bytes),
701            Self::FixedBytes(word, size) => buf.extend_from_slice(&word[..(*size).min(32)]),
702            Self::Int(num, size) => {
703                let byte_size = *size / 8;
704                let start = 32usize.saturating_sub(byte_size);
705                buf.extend_from_slice(&num.to_be_bytes::<32>()[start..]);
706            }
707            Self::Uint(num, size) => {
708                let byte_size = *size / 8;
709                let start = 32usize.saturating_sub(byte_size);
710                buf.extend_from_slice(&num.to_be_bytes::<32>()[start..]);
711            }
712            Self::FixedArray(inner) | Self::Array(inner) => {
713                for val in inner {
714                    // Array elements are left-padded to 32 bytes.
715                    if let Some(padding_needed) = 32usize.checked_sub(val.abi_packed_encoded_size())
716                    {
717                        buf.extend(core::iter::repeat_n(0, padding_needed));
718                    }
719                    val.abi_encode_packed_to(buf);
720                }
721            }
722            as_tuple!(Self inner) => {
723                for val in inner {
724                    val.abi_encode_packed_to(buf);
725                }
726            }
727        }
728    }
729
730    /// Returns the length of this value when ABI-encoded in Non-standard Packed Mode.
731    ///
732    /// See [`abi_encode_packed`](Self::abi_encode_packed) for more details.
733    pub fn abi_packed_encoded_size(&self) -> usize {
734        match self {
735            Self::Address(_) | Self::Function(_) => 20,
736            Self::Bool(_) => 1,
737            Self::String(s) => s.len(),
738            Self::Bytes(b) => b.len(),
739            Self::FixedBytes(_, size) => (*size).min(32),
740            Self::Int(_, size) | Self::Uint(_, size) => (size / 8).min(32),
741            Self::FixedArray(inner) | Self::Array(inner) => {
742                inner.iter().map(|v| v.abi_packed_encoded_size().max(32)).sum()
743            }
744            as_tuple!(Self inner) => inner.iter().map(Self::abi_packed_encoded_size).sum(),
745        }
746    }
747
748    /// Tokenize this value into a [`DynToken`].
749    pub fn tokenize(&self) -> DynToken<'_> {
750        match self {
751            Self::Address(a) => a.into_word().into(),
752            Self::Function(f) => f.into_word().into(),
753            Self::Bool(b) => Word::with_last_byte(*b as u8).into(),
754            Self::Bytes(buf) => DynToken::PackedSeq(buf),
755            Self::FixedBytes(buf, _) => (*buf).into(),
756            Self::Int(int, _) => int.to_be_bytes::<32>().into(),
757            Self::Uint(uint, _) => uint.to_be_bytes::<32>().into(),
758            Self::String(s) => DynToken::PackedSeq(s.as_bytes()),
759            Self::Array(t) => DynToken::from_dyn_seq(t),
760            as_fixed_seq!(t) => DynToken::from_fixed_seq(t),
761        }
762    }
763
764    /// Encode this data as a sequence.
765    pub(crate) fn encode_seq(seq: &[Self]) -> Vec<u8> {
766        let sz = seq.iter().map(Self::total_words).sum();
767        let mut encoder = Encoder::with_capacity(sz);
768        Self::encode_seq_to(seq, &mut encoder);
769        encoder.into_bytes()
770    }
771
772    /// Encode this data as a sequence into the given encoder.
773    pub(crate) fn encode_seq_to(contents: &[Self], enc: &mut Encoder) {
774        let head_words = contents.iter().map(Self::head_words).sum::<usize>();
775        enc.push_offset(head_words);
776
777        for t in contents {
778            t.head_append(enc);
779            enc.bump_offset(t.tail_words());
780        }
781
782        for t in contents {
783            t.tail_append(enc);
784        }
785
786        enc.pop_offset();
787    }
788
789    /// Encode this value into a byte array by wrapping it into a 1-element
790    /// sequence.
791    #[inline]
792    pub fn abi_encode(&self) -> Vec<u8> {
793        Self::encode_seq(core::slice::from_ref(self))
794    }
795
796    /// Encode this value into a byte array suitable for passing to a function.
797    /// If this value is a tuple, it is encoded as is. Otherwise, it is wrapped
798    /// into a 1-element sequence.
799    ///
800    /// # Examples
801    ///
802    /// ```ignore (pseudo-code)
803    /// // Encoding for function foo(address)
804    /// DynSolValue::Address(_).abi_encode_params();
805    ///
806    /// // Encoding for function foo(address, uint256)
807    /// DynSolValue::Tuple(vec![
808    ///     DynSolValue::Address(_),
809    ///     DynSolValue::Uint(_, 256),
810    /// ]).abi_encode_params();
811    /// ```
812    #[inline]
813    pub fn abi_encode_params(&self) -> Vec<u8> {
814        match self {
815            Self::Tuple(seq) => Self::encode_seq(seq),
816            _ => self.abi_encode(),
817        }
818    }
819
820    /// If this value is a fixed sequence, encode it into a byte array. If this
821    /// value is not a fixed sequence, return `None`.
822    #[inline]
823    pub fn abi_encode_sequence(&self) -> Option<Vec<u8>> {
824        self.as_fixed_seq().map(Self::encode_seq)
825    }
826}