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