1#![allow(clippy::arc_with_non_send_sync)]
11
12use crate::{DynSolType, DynSolValue};
13use alloy_primitives::{Address, Function, B256, I256, U256};
14use arbitrary::{size_hint, Unstructured};
15use core::ops::RangeInclusive;
16use proptest::{
17 collection::{vec as vec_strategy, VecStrategy},
18 prelude::*,
19 strategy::{Flatten, Map, Recursive, TupleUnion, WA},
20};
21
22const DEPTH: u32 = 16;
23const DESIRED_SIZE: u32 = 64;
24const EXPECTED_BRANCH_SIZE: u32 = 32;
25
26macro_rules! prop_oneof_cfg {
27 ($($(@[$attr:meta])* $w:expr => $x:expr,)+) => {
28 TupleUnion::new(($(
29 $(#[$attr])*
30 {
31 ($w as u32, ::alloc::sync::Arc::new($x))
32 }
33 ),+))
34 };
35}
36
37#[cfg(not(feature = "eip712"))]
38macro_rules! tuple_type_cfg {
39 (($($t:ty),+ $(,)?), $c:ty $(,)?) => {
40 ($($t,)+)
41 };
42}
43#[cfg(feature = "eip712")]
44macro_rules! tuple_type_cfg {
45 (($($t:ty),+ $(,)?), $c:ty $(,)?) => {
46 ($($t,)+ $c)
47 };
48}
49
50#[inline]
51const fn int_size(n: usize) -> usize {
52 let n = (n % 255) + 1;
53 n + (8 - (n % 8))
54}
55
56#[inline]
57#[cfg(feature = "eip712")]
58const fn ident_char(x: u8, first: bool) -> u8 {
59 let x = x % 64;
60 match x {
61 0..=25 => x + b'a',
62 26..=51 => (x - 26) + b'A',
63 52 => b'_',
64 53 => b'$',
65 _ => {
66 if first {
67 b'a'
68 } else {
69 (x - 54) + b'0'
70 }
71 }
72 }
73}
74
75fn non_empty_vec<'a, T: arbitrary::Arbitrary<'a>>(
76 u: &mut Unstructured<'a>,
77) -> arbitrary::Result<Vec<T>> {
78 let sz = u.int_in_range(1..=16u8)?;
79 let mut v = Vec::with_capacity(sz as usize);
80 for _ in 0..sz {
81 v.push(u.arbitrary()?);
82 }
83 Ok(v)
84}
85
86#[cfg(feature = "eip712")]
87struct AString(String);
88
89#[cfg(feature = "eip712")]
90impl<'a> arbitrary::Arbitrary<'a> for AString {
91 #[inline]
92 #[cfg_attr(debug_assertions, track_caller)]
93 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
94 let len = u.int_in_range(1..=128)?;
97 let mut bytes = Vec::with_capacity(len);
98 for i in 0..len {
99 bytes.push(ident_char(u.arbitrary()?, i == 0));
100 }
101 Ok(Self::new(bytes))
102 }
103
104 #[inline]
105 #[cfg_attr(debug_assertions, track_caller)]
106 fn arbitrary_take_rest(u: Unstructured<'a>) -> arbitrary::Result<Self> {
107 let mut bytes = u.take_rest().to_owned();
108 for (i, byte) in bytes.iter_mut().enumerate() {
109 *byte = ident_char(*byte, i == 0);
110 }
111 Ok(Self::new(bytes))
112 }
113
114 #[inline]
115 fn size_hint(depth: usize) -> (usize, Option<usize>) {
116 String::size_hint(depth)
117 }
118}
119
120#[cfg(feature = "eip712")]
121impl AString {
122 #[inline]
123 #[cfg_attr(debug_assertions, track_caller)]
124 fn new(bytes: Vec<u8>) -> Self {
125 debug_assert!(core::str::from_utf8(&bytes).is_ok());
126 Self(unsafe { String::from_utf8_unchecked(bytes) })
127 }
128}
129
130#[derive(Debug, derive_arbitrary::Arbitrary)]
131enum Choice {
132 Bool,
133 Int,
134 Uint,
135 Address,
136 Function,
137 FixedBytes,
138 Bytes,
139 String,
140
141 Array,
142 FixedArray,
143 Tuple,
144 #[cfg(feature = "eip712")]
145 CustomStruct,
146}
147
148impl<'a> arbitrary::Arbitrary<'a> for DynSolType {
149 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
150 match u.arbitrary::<Choice>()? {
151 Choice::Bool => Ok(Self::Bool),
152 Choice::Int => u.arbitrary().map(int_size).map(Self::Int),
153 Choice::Uint => u.arbitrary().map(int_size).map(Self::Uint),
154 Choice::Address => Ok(Self::Address),
155 Choice::Function => Ok(Self::Function),
156 Choice::FixedBytes => Ok(Self::FixedBytes(u.int_in_range(1..=32)?)),
157 Choice::Bytes => Ok(Self::Bytes),
158 Choice::String => Ok(Self::String),
159 Choice::Array => u.arbitrary().map(Self::Array),
160 Choice::FixedArray => Ok(Self::FixedArray(u.arbitrary()?, u.int_in_range(1..=16)?)),
161 Choice::Tuple => non_empty_vec(u).map(Self::Tuple),
162 #[cfg(feature = "eip712")]
163 Choice::CustomStruct => {
164 let name = u.arbitrary::<AString>()?.0;
165 let (prop_names, tuple) =
166 u.arbitrary_iter::<(AString, Self)>()?.flatten().map(|(a, b)| (a.0, b)).unzip();
167 Ok(Self::CustomStruct { name, prop_names, tuple })
168 }
169 }
170 }
171
172 fn size_hint(depth: usize) -> (usize, Option<usize>) {
173 if depth == DEPTH as usize {
174 (0, Some(0))
175 } else {
176 size_hint::and(
177 u32::size_hint(depth),
178 size_hint::or_all(&[usize::size_hint(depth), Self::size_hint(depth + 1)]),
179 )
180 }
181 }
182}
183
184impl<'a> arbitrary::Arbitrary<'a> for DynSolValue {
185 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
186 match u.arbitrary::<DynSolType>()? {
187 #[cfg(feature = "eip712")]
189 DynSolType::CustomStruct { name, prop_names, tuple } => Ok(Self::CustomStruct {
190 name,
191 prop_names,
192 tuple: tuple
193 .iter()
194 .map(|ty| Self::arbitrary_from_type(ty, u))
195 .collect::<Result<_, _>>()?,
196 }),
197 t => Self::arbitrary_from_type(&t, u),
198 }
199 }
200
201 fn size_hint(depth: usize) -> (usize, Option<usize>) {
202 if depth == DEPTH as usize {
203 (0, Some(0))
204 } else {
205 size_hint::and(
206 u32::size_hint(depth),
207 size_hint::or_all(&[
208 B256::size_hint(depth),
209 usize::size_hint(depth),
210 Self::size_hint(depth + 1),
211 ]),
212 )
213 }
214 }
215}
216
217type ValueOfStrategy<S> = <S as Strategy>::Value;
219
220type StratMap<S, T> = Map<S, fn(ValueOfStrategy<S>) -> T>;
221
222type MappedWA<S, T> = WA<StratMap<S, T>>;
223
224type Flat<S, T> = Flatten<StratMap<S, T>>;
225
226type Rec<T, S> = Recursive<T, fn(BoxedStrategy<T>) -> S>;
227
228#[cfg(feature = "eip712")]
229const IDENT_STRATEGY: &str = parser::IDENT_REGEX;
230#[cfg(feature = "eip712")]
231type CustomStructStrategy<T> = BoxedStrategy<T>;
232
233#[cfg(feature = "eip712")]
234macro_rules! custom_struct_strategy {
235 ($range:expr, $elem:expr) => {{
236 let range: RangeInclusive<usize> = $range;
238 let elem: BoxedStrategy<Self> = $elem;
239 let strat: CustomStructStrategy<Self> = range
240 .prop_flat_map(move |sz| {
241 (
242 IDENT_STRATEGY,
243 proptest::collection::hash_set(IDENT_STRATEGY, sz..=sz)
244 .prop_map(|prop_names| prop_names.into_iter().collect()),
245 vec_strategy(elem.clone(), sz..=sz),
246 )
247 })
248 .prop_map(|(name, prop_names, tuple)| Self::CustomStruct { name, prop_names, tuple })
249 .boxed();
250 strat
251 }};
252}
253
254type TypeRecurseStrategy = TupleUnion<
256 tuple_type_cfg![
257 (
258 WA<BoxedStrategy<DynSolType>>, MappedWA<BoxedStrategy<DynSolType>, DynSolType>, MappedWA<(BoxedStrategy<DynSolType>, RangeInclusive<usize>), DynSolType>, MappedWA<VecStrategy<BoxedStrategy<DynSolType>>, DynSolType>, ),
263 WA<CustomStructStrategy<DynSolType>>, ],
265>;
266type TypeStrategy = Rec<DynSolType, TypeRecurseStrategy>;
267
268type ValueArrayStrategy =
269 Flat<BoxedStrategy<DynSolValue>, VecStrategy<SBoxedStrategy<DynSolValue>>>;
270
271type ValueRecurseStrategy = TupleUnion<
272 tuple_type_cfg![
273 (
274 WA<BoxedStrategy<DynSolValue>>, MappedWA<ValueArrayStrategy, DynSolValue>, MappedWA<ValueArrayStrategy, DynSolValue>, MappedWA<VecStrategy<BoxedStrategy<DynSolValue>>, DynSolValue>, ),
279 WA<CustomStructStrategy<DynSolValue>>, ],
281>;
282type ValueStrategy = Rec<DynSolValue, ValueRecurseStrategy>;
283
284impl proptest::arbitrary::Arbitrary for DynSolType {
285 type Parameters = (u32, u32, u32);
286 type Strategy = TypeStrategy;
287
288 #[inline]
289 fn arbitrary() -> Self::Strategy {
290 Self::arbitrary_with((DEPTH, DESIRED_SIZE, EXPECTED_BRANCH_SIZE))
291 }
292
293 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
294 let (depth, desired_size, expected_branch_size) = args;
295 Self::leaf().prop_recursive(depth, desired_size, expected_branch_size, Self::recurse)
296 }
297}
298
299impl DynSolType {
300 #[inline]
302 pub fn arbitrary_value(&self, u: &mut Unstructured<'_>) -> arbitrary::Result<DynSolValue> {
303 DynSolValue::arbitrary_from_type(self, u)
304 }
305
306 #[inline]
309 pub fn value_strategy(&self) -> SBoxedStrategy<DynSolValue> {
310 DynSolValue::type_strategy(self)
311 }
312
313 #[inline]
314 fn leaf() -> impl Strategy<Value = Self> {
315 prop_oneof![
316 Just(Self::Bool),
317 Just(Self::Address),
318 any::<usize>().prop_map(|x| Self::Int(int_size(x))),
319 any::<usize>().prop_map(|x| Self::Uint(int_size(x))),
320 (1..=32usize).prop_map(Self::FixedBytes),
321 Just(Self::Bytes),
322 Just(Self::String),
323 ]
324 }
325
326 #[inline]
327 fn recurse(element: BoxedStrategy<Self>) -> TypeRecurseStrategy {
328 prop_oneof_cfg![
329 1 => element.clone(),
330 2 => element.clone().prop_map(|ty| Self::Array(Box::new(ty))),
331 2 => (element.clone(), 1..=16).prop_map(|(ty, sz)| Self::FixedArray(Box::new(ty), sz)),
332 2 => vec_strategy(element.clone(), 1..=16).prop_map(Self::Tuple),
333 @[cfg(feature = "eip712")]
334 1 => custom_struct_strategy!(1..=16, element),
335 ]
336 }
337}
338
339impl proptest::arbitrary::Arbitrary for DynSolValue {
340 type Parameters = (u32, u32, u32);
341 type Strategy = ValueStrategy;
342
343 #[inline]
344 fn arbitrary() -> Self::Strategy {
345 Self::arbitrary_with((DEPTH, DESIRED_SIZE, EXPECTED_BRANCH_SIZE))
346 }
347
348 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
349 let (depth, desired_size, expected_branch_size) = args;
350 Self::leaf().prop_recursive(depth, desired_size, expected_branch_size, Self::recurse)
351 }
352}
353
354impl DynSolValue {
355 pub fn arbitrary_from_type(
357 ty: &DynSolType,
358 u: &mut Unstructured<'_>,
359 ) -> arbitrary::Result<Self> {
360 match ty {
361 DynSolType::Bool => u.arbitrary().map(Self::Bool),
362 DynSolType::Address => u.arbitrary().map(Self::Address),
363 DynSolType::Function => u.arbitrary().map(Self::Function),
364 &DynSolType::Int(sz) => u.arbitrary().map(|x| Self::Int(adjust_int(x, sz), sz)),
365 &DynSolType::Uint(sz) => u.arbitrary().map(|x| Self::Uint(adjust_uint(x, sz), sz)),
366 &DynSolType::FixedBytes(sz) => {
367 u.arbitrary().map(|x| Self::FixedBytes(adjust_fb(x, sz), sz))
368 }
369 DynSolType::Bytes => u.arbitrary().map(Self::Bytes),
370 DynSolType::String => u.arbitrary().map(Self::String),
371 DynSolType::Array(ty) => {
372 let sz = u.int_in_range(1..=16u8)?;
373 let mut v = Vec::with_capacity(sz as usize);
374 for _ in 0..sz {
375 v.push(Self::arbitrary_from_type(ty, u)?);
376 }
377 Ok(Self::Array(v))
378 }
379 &DynSolType::FixedArray(ref ty, sz) => {
380 let mut v = Vec::with_capacity(sz);
381 for _ in 0..sz {
382 v.push(Self::arbitrary_from_type(ty, u)?);
383 }
384 Ok(Self::FixedArray(v))
385 }
386 DynSolType::Tuple(tuple) => tuple
387 .iter()
388 .map(|ty| Self::arbitrary_from_type(ty, u))
389 .collect::<Result<Vec<_>, _>>()
390 .map(Self::Tuple),
391 #[cfg(feature = "eip712")]
392 DynSolType::CustomStruct { tuple, .. } => {
393 let name = u.arbitrary::<AString>()?.0;
394 let tuple = tuple
395 .iter()
396 .map(|ty| Self::arbitrary_from_type(ty, u))
397 .collect::<Result<Vec<_>, _>>()?;
398 let sz = tuple.len();
399 let prop_names = (0..sz)
400 .map(|_| u.arbitrary::<AString>().map(|s| s.0))
401 .collect::<Result<Vec<_>, _>>()?;
402 Ok(Self::CustomStruct { name, prop_names, tuple })
403 }
404 }
405 }
406
407 pub fn type_strategy(ty: &DynSolType) -> SBoxedStrategy<Self> {
410 match ty {
411 DynSolType::Bool => any::<bool>().prop_map(Self::Bool).sboxed(),
412 DynSolType::Address => any::<Address>().prop_map(Self::Address).sboxed(),
413 DynSolType::Function => any::<Function>().prop_map(Self::Function).sboxed(),
414 &DynSolType::Int(sz) => {
415 any::<I256>().prop_map(move |x| Self::Int(adjust_int(x, sz), sz)).sboxed()
416 }
417 &DynSolType::Uint(sz) => {
418 any::<U256>().prop_map(move |x| Self::Uint(adjust_uint(x, sz), sz)).sboxed()
419 }
420 &DynSolType::FixedBytes(sz) => {
421 any::<B256>().prop_map(move |x| Self::FixedBytes(adjust_fb(x, sz), sz)).sboxed()
422 }
423 DynSolType::Bytes => any::<Vec<u8>>().prop_map(Self::Bytes).sboxed(),
424 DynSolType::String => any::<String>().prop_map(Self::String).sboxed(),
425 DynSolType::Array(ty) => {
426 let element = Self::type_strategy(ty);
427 vec_strategy(element, 1..=16).prop_map(Self::Array).sboxed()
428 }
429 DynSolType::FixedArray(ty, sz) => {
430 let element = Self::type_strategy(ty);
431 vec_strategy(element, *sz).prop_map(Self::FixedArray).sboxed()
432 }
433 DynSolType::Tuple(tys) => tys
434 .iter()
435 .map(Self::type_strategy)
436 .collect::<Vec<_>>()
437 .prop_map(Self::Tuple)
438 .sboxed(),
439 #[cfg(feature = "eip712")]
440 DynSolType::CustomStruct { tuple, prop_names, name } => {
441 let name = name.clone();
442 let prop_names = prop_names.clone();
443 tuple
444 .iter()
445 .map(Self::type_strategy)
446 .collect::<Vec<_>>()
447 .prop_map(move |tuple| Self::CustomStruct {
448 name: name.clone(),
449 prop_names: prop_names.clone(),
450 tuple,
451 })
452 .sboxed()
453 }
454 }
455 }
456
457 #[inline]
460 pub fn value_strategy(&self) -> SBoxedStrategy<Self> {
461 Self::type_strategy(&self.as_type().unwrap())
462 }
463
464 #[inline]
465 fn leaf() -> impl Strategy<Value = Self> {
466 prop_oneof![
467 any::<bool>().prop_map(Self::Bool),
468 any::<Address>().prop_map(Self::Address),
469 int_strategy::<I256>().prop_map(|(x, sz)| Self::Int(adjust_int(x, sz), sz)),
470 int_strategy::<U256>().prop_map(|(x, sz)| Self::Uint(adjust_uint(x, sz), sz)),
471 (any::<B256>(), 1..=32usize).prop_map(|(x, sz)| Self::FixedBytes(adjust_fb(x, sz), sz)),
472 any::<Vec<u8>>().prop_map(Self::Bytes),
473 any::<String>().prop_map(Self::String),
474 ]
475 }
476
477 #[inline]
478 fn recurse(element: BoxedStrategy<Self>) -> ValueRecurseStrategy {
479 prop_oneof_cfg![
480 1 => element.clone(),
481 2 => Self::array_strategy(element.clone()).prop_map(Self::Array),
482 2 => Self::array_strategy(element.clone()).prop_map(Self::FixedArray),
483 2 => vec_strategy(element.clone(), 1..=16).prop_map(Self::Tuple),
484 @[cfg(feature = "eip712")]
485 1 => custom_struct_strategy!(1..=16, element),
486 ]
487 }
488
489 #[inline]
515 #[allow(rustdoc::invalid_rust_codeblocks)]
516 fn array_strategy(element: BoxedStrategy<Self>) -> ValueArrayStrategy {
517 element.prop_flat_map(|x| vec_strategy(x.value_strategy(), 1..=16))
518 }
519}
520
521#[inline]
522fn int_strategy<T: Arbitrary>() -> impl Strategy<Value = (ValueOfStrategy<T::Strategy>, usize)> {
523 (any::<T>(), any::<usize>().prop_map(int_size))
524}
525
526#[inline]
528fn adjust_int(mut int: I256, size: usize) -> I256 {
529 if size < 256 {
530 if int.bit(size - 1) {
531 int |= I256::MINUS_ONE - (I256::ONE << size).wrapping_sub(I256::ONE);
532 } else {
533 int &= (I256::ONE << size).wrapping_sub(I256::ONE);
534 }
535 }
536 int
537}
538
539#[inline]
540fn adjust_uint(mut uint: U256, size: usize) -> U256 {
541 if size < 256 {
542 uint &= (U256::from(1u64) << size).wrapping_sub(U256::from(1u64));
543 }
544 uint
545}
546
547#[inline]
548fn adjust_fb(mut word: B256, size: usize) -> B256 {
549 if size < 32 {
550 word[size..].fill(0);
551 }
552 word
553}
554
555#[cfg(all(test, not(miri)))] mod tests {
557 use super::*;
558 #[cfg(feature = "eip712")]
559 use parser::{is_id_continue, is_id_start, is_valid_identifier};
560
561 proptest! {
562 #![proptest_config(ProptestConfig {
563 cases: 1024,
564 ..Default::default()
565 })]
566
567 #[test]
568 fn int_size(x: usize) {
569 let sz = super::int_size(x);
570 prop_assert!(sz > 0 && sz <= 256, "{sz}");
571 prop_assert!(sz % 8 == 0, "{sz}");
572 }
573
574 #[test]
575 #[cfg(feature = "eip712")]
576 fn ident_char(x: u8) {
577 let start = super::ident_char(x, true);
578 prop_assert!(is_id_start(start as char));
579 prop_assert!(is_id_continue(start as char));
580
581 let cont = super::ident_char(x, false);
582 prop_assert!(is_id_continue(cont as char));
583 }
584 }
585
586 proptest! {
587 #![proptest_config(ProptestConfig {
588 cases: 256,
589 ..Default::default()
590 })]
591
592 #[test]
593 #[cfg(feature = "eip712")]
594 fn arbitrary_string(bytes: Vec<u8>) {
595 prop_assume!(!bytes.is_empty());
596 let mut u = Unstructured::new(&bytes);
597
598 let s = u.arbitrary::<AString>();
599 prop_assume!(s.is_ok());
600
601 let s = s.unwrap().0;
602 prop_assume!(!s.is_empty());
603
604 prop_assert!(
605 is_valid_identifier(&s),
606 "not a valid identifier: {:?}\ndata: {}",
607 s,
608 hex::encode_prefixed(&bytes),
609 );
610 }
611
612 #[test]
613 fn arbitrary_type(bytes: Vec<u8>) {
614 prop_assume!(!bytes.is_empty());
615 let mut u = Unstructured::new(&bytes);
616 let ty = u.arbitrary::<DynSolType>();
617 prop_assume!(ty.is_ok());
618 type_test(ty.unwrap())?;
619 }
620
621 #[test]
622 fn arbitrary_value(bytes: Vec<u8>) {
623 prop_assume!(!bytes.is_empty());
624 let mut u = Unstructured::new(&bytes);
625 let value = u.arbitrary::<DynSolValue>();
626 prop_assume!(value.is_ok());
627 value_test(value.unwrap())?;
628 }
629
630 #[test]
631 fn proptest_type(ty: DynSolType) {
632 type_test(ty)?;
633 }
634
635 #[test]
636 fn proptest_value(value: DynSolValue) {
637 value_test(value)?;
638 }
639 }
640
641 fn type_test(ty: DynSolType) -> Result<(), TestCaseError> {
642 let s = ty.sol_type_name();
643 prop_assume!(!ty.has_custom_struct());
644 prop_assert_eq!(DynSolType::parse(&s), Ok(ty), "type strings don't match");
645 Ok(())
646 }
647
648 fn value_test(value: DynSolValue) -> Result<(), TestCaseError> {
649 let ty = match value.as_type() {
650 Some(ty) => ty,
651 None => {
652 prop_assert!(false, "generated invalid type: {value:?}");
653 unreachable!()
654 }
655 };
656 let s = value.sol_type_name().unwrap();
658
659 prop_assert_eq!(&s, &ty.sol_type_name(), "type strings don't match");
660
661 assert_valid_value(&value)?;
662
663 if !ty.has_custom_struct() {
665 let parsed = s.parse::<DynSolType>();
666 prop_assert_eq!(parsed.as_ref(), Ok(&ty), "types don't match {:?}", s);
667 }
668
669 let data = value.abi_encode_params();
670 match ty.abi_decode_params(&data) {
671 Ok(decoded) if !decoded.has_custom_struct() => prop_assert_eq!(
674 &decoded,
675 &value,
676 "\n\ndecoded value doesn't match {:?} ({:?})\ndata: {:?}",
677 s,
678 ty,
679 hex::encode_prefixed(&data),
680 ),
681 Ok(_) => {}
682 Err(e @ crate::Error::SolTypes(alloy_sol_types::Error::RecursionLimitExceeded(_))) => {
683 return Err(TestCaseError::Reject(e.to_string().into()));
684 }
685 Err(e) => prop_assert!(
686 false,
687 "failed to decode {s:?}: {e}\nvalue: {value:?}\ndata: {:?}",
688 hex::encode_prefixed(&data),
689 ),
690 }
691
692 Ok(())
693 }
694
695 pub(crate) fn assert_valid_value(value: &DynSolValue) -> Result<(), TestCaseError> {
696 match &value {
697 DynSolValue::Array(values) | DynSolValue::FixedArray(values) => {
698 prop_assert!(!values.is_empty());
699 let mut values = values.iter();
700 let ty = values.next().unwrap().as_type().unwrap();
701 prop_assert!(
702 values.all(|v| ty.matches(v)),
703 "array elements have different types: {value:#?}",
704 );
705 }
706 #[cfg(feature = "eip712")]
707 DynSolValue::CustomStruct { name, prop_names, tuple } => {
708 prop_assert!(is_valid_identifier(name));
709 prop_assert!(prop_names.iter().all(|s| is_valid_identifier(s)));
710 prop_assert_eq!(prop_names.len(), tuple.len());
711 }
712 _ => {}
713 }
714
715 match value {
716 DynSolValue::Int(int, size) => {
717 let bits = int.into_sign_and_abs().1.bit_len();
718 prop_assert!(bits <= *size, "int: {int}, {size}, {bits}")
719 }
720 DynSolValue::Uint(uint, size) => {
721 let bits = uint.bit_len();
722 prop_assert!(bits <= *size, "uint: {uint}, {size}, {bits}")
723 }
724 DynSolValue::FixedBytes(fb, size) => {
725 prop_assert!(fb[*size..].iter().all(|x| *x == 0), "fb {fb}, {size}")
726 }
727 _ => {}
728 }
729
730 match value {
732 DynSolValue::Array(t)
733 | DynSolValue::FixedArray(t)
734 | crate::dynamic::ty::as_tuple!(DynSolValue t) => {
735 t.iter().try_for_each(assert_valid_value)?
736 }
737 _ => {}
738 }
739
740 Ok(())
741 }
742}