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#[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#[derive(Clone, Debug, PartialEq)]
61pub enum DynSolValue {
62 Bool(bool),
64 Int(I256, usize),
66 Uint(U256, usize),
68 FixedBytes(Word, usize),
70 Address(Address),
72 Function(Function),
74
75 Bytes(Vec<u8>),
77 String(String),
79
80 Array(Vec<Self>),
82 FixedArray(Vec<Self>),
84 Tuple(Vec<Self>),
86
87 #[cfg(feature = "eip712")]
89 CustomStruct {
90 name: String,
92 prop_names: Vec<String>,
94 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[inline]
388 pub fn as_bytes(&self) -> Option<&[u8]> {
389 match self {
390 Self::Bytes(b) => Some(b),
391 _ => None,
392 }
393 }
394
395 #[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 #[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 #[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 #[inline]
424 pub fn as_str(&self) -> Option<&str> {
425 match self {
426 Self::String(s) => Some(s),
427 _ => None,
428 }
429 }
430
431 #[inline]
433 pub fn as_tuple(&self) -> Option<&[Self]> {
434 match self {
435 Self::Tuple(t) => Some(t),
436 _ => None,
437 }
438 }
439
440 #[inline]
442 pub fn as_array(&self) -> Option<&[Self]> {
443 match self {
444 Self::Array(a) => Some(a),
445 _ => None,
446 }
447 }
448
449 #[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 #[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 #[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 #[inline]
491 pub const fn is_sequence(&self) -> bool {
492 matches!(self, as_fixed_seq!(_) | Self::Array(_))
493 }
494
495 #[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 #[inline]
507 #[allow(clippy::missing_const_for_fn)] 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 #[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 #[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 #[doc(alias = "types_check")] #[inline(always)]
545 pub fn matches_many(values: &[Self], types: &[DynSolType]) -> bool {
546 DynSolType::matches_many(types, values)
547 }
548
549 #[doc(alias = "type_check")] #[inline(always)]
554 pub fn matches(&self, ty: &DynSolType) -> bool {
555 ty.matches(self)
556 }
557
558 #[inline]
560 pub(crate) fn head_words(&self) -> usize {
561 match self.as_fixed_seq() {
562 Some(vals) => {
564 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 None => 1,
577 }
578 }
579
580 #[inline]
582 pub(crate) fn tail_words(&self) -> usize {
583 match self {
584 Self::Address(_)
586 | Self::Function(_)
587 | Self::Bool(_)
588 | Self::FixedBytes(..)
589 | Self::Int(..)
590 | Self::Uint(..) => 0,
591
592 Self::String(s) => 1 + words_for_len(s.len()),
595 Self::Bytes(b) => 1 + words_for_len(b.len()),
596
597 as_fixed_seq!(tuple) => {
601 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::Array(vals) => 1 + vals.iter().map(Self::total_words).sum::<usize>(),
615 }
616 }
617
618 #[inline]
621 pub(crate) fn total_words(&self) -> usize {
622 self.head_words() + self.tail_words()
623 }
624
625 #[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 #[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 #[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 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 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 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 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 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 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 #[inline]
792 pub fn abi_encode(&self) -> Vec<u8> {
793 Self::encode_seq(core::slice::from_ref(self))
794 }
795
796 #[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 #[inline]
823 pub fn abi_encode_sequence(&self) -> Option<Vec<u8>> {
824 self.as_fixed_seq().map(Self::encode_seq)
825 }
826}