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<DynSolValue>),
82 FixedArray(Vec<DynSolValue>),
84 Tuple(Vec<DynSolValue>),
86
87 #[cfg(feature = "eip712")]
89 CustomStruct {
90 name: String,
92 prop_names: Vec<String>,
94 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[inline]
381 pub fn as_bytes(&self) -> Option<&[u8]> {
382 match self {
383 Self::Bytes(b) => Some(b),
384 _ => None,
385 }
386 }
387
388 #[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 #[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 #[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 #[inline]
417 pub fn as_str(&self) -> Option<&str> {
418 match self {
419 Self::String(s) => Some(s),
420 _ => None,
421 }
422 }
423
424 #[inline]
426 pub fn as_tuple(&self) -> Option<&[Self]> {
427 match self {
428 Self::Tuple(t) => Some(t),
429 _ => None,
430 }
431 }
432
433 #[inline]
435 pub fn as_array(&self) -> Option<&[Self]> {
436 match self {
437 Self::Array(a) => Some(a),
438 _ => None,
439 }
440 }
441
442 #[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 #[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 #[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 #[inline]
484 pub const fn is_sequence(&self) -> bool {
485 matches!(self, as_fixed_seq!(_) | Self::Array(_))
486 }
487
488 #[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 #[inline]
500 #[allow(clippy::missing_const_for_fn)] 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 #[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 #[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 #[doc(alias = "types_check")] #[inline(always)]
538 pub fn matches_many(values: &[Self], types: &[DynSolType]) -> bool {
539 DynSolType::matches_many(types, values)
540 }
541
542 #[doc(alias = "type_check")] #[inline(always)]
547 pub fn matches(&self, ty: &DynSolType) -> bool {
548 ty.matches(self)
549 }
550
551 #[inline]
553 pub(crate) fn head_words(&self) -> usize {
554 match self.as_fixed_seq() {
555 Some(vals) => {
557 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 None => 1,
570 }
571 }
572
573 #[inline]
575 pub(crate) fn tail_words(&self) -> usize {
576 match self {
577 Self::Address(_)
579 | Self::Function(_)
580 | Self::Bool(_)
581 | Self::FixedBytes(..)
582 | Self::Int(..)
583 | Self::Uint(..) => 0,
584
585 Self::String(s) => 1 + words_for_len(s.len()),
588 Self::Bytes(b) => 1 + words_for_len(b.len()),
589
590 as_fixed_seq!(tuple) => {
594 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::Array(vals) => 1 + vals.iter().map(Self::total_words).sum::<usize>(),
608 }
609 }
610
611 #[inline]
614 pub(crate) fn total_words(&self) -> usize {
615 self.head_words() + self.tail_words()
616 }
617
618 #[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 #[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 #[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 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 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 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 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 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 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 #[inline]
785 pub fn abi_encode(&self) -> Vec<u8> {
786 Self::encode_seq(core::slice::from_ref(self))
787 }
788
789 #[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 #[inline]
816 pub fn abi_encode_sequence(&self) -> Option<Vec<u8>> {
817 self.as_fixed_seq().map(Self::encode_seq)
818 }
819}