Skip to main content

foundry_evm_symbolic/
abi.rs

1use super::{runtime::*, *};
2
3#[derive(Clone, Debug)]
4pub(super) struct SymbolicCalldata {
5    pub(super) size: usize,
6    pub(super) bytes: Vec<SymWord>,
7    pub(super) inputs: Vec<SymbolicInput>,
8    pub(super) constraints: Vec<BoolExpr>,
9}
10
11impl SymbolicCalldata {
12    /// Constructs a new instance.
13    #[cfg(test)]
14    pub(super) fn new(function: &Function, config: &SymbolicConfig) -> Result<Self, SymbolicError> {
15        Ok(Self::variants(function, config)?.remove(0))
16    }
17
18    /// Returns the `variants` symbolic ABI helper result.
19    pub(super) fn variants(
20        function: &Function,
21        config: &SymbolicConfig,
22    ) -> Result<Vec<Self>, SymbolicError> {
23        Self::variants_with_prefix(function, config, "calldata")
24    }
25
26    /// Returns the `selector_only` symbolic ABI helper result.
27    pub(super) fn selector_only(function: &Function) -> Result<Self, SymbolicError> {
28        if !function.inputs.is_empty() {
29            return Err(SymbolicError::UnsupportedAbi(format!(
30                "symbolic invariant `{}` must take no parameters",
31                function.name
32            )));
33        }
34        let bytes = function
35            .selector()
36            .iter()
37            .copied()
38            .map(|byte| SymWord::Concrete(U256::from(byte)))
39            .collect::<Vec<_>>();
40        Ok(Self { size: bytes.len(), bytes, inputs: Vec::new(), constraints: Vec::new() })
41    }
42
43    /// Implements the `variants_with_prefix` symbolic ABI helper.
44    pub(super) fn variants_with_prefix(
45        function: &Function,
46        config: &SymbolicConfig,
47        prefix: impl AsRef<str>,
48    ) -> Result<Vec<Self>, SymbolicError> {
49        let prefix = prefix.as_ref();
50        let variant_limit = calldata_variant_limit(config);
51        let mut variants = vec![(SymbolicAbiBuilder::new(config), Vec::new())];
52        for (idx, input) in function.inputs.iter().enumerate() {
53            let ty = input.selector_type();
54            let mut next_variants = Vec::new();
55            for (builder, inputs) in variants {
56                for (builder, input) in SymbolicInput::variants(
57                    builder,
58                    prefix,
59                    idx,
60                    Some(input.name.as_str()),
61                    ty.as_ref(),
62                )? {
63                    let mut inputs = inputs.clone();
64                    inputs.push(input);
65                    push_variant(&mut next_variants, (builder, inputs), variant_limit)?;
66                }
67            }
68            variants = next_variants;
69        }
70
71        validate_positional_dynamic_lengths(
72            config,
73            variants.iter().map(|(builder, _)| builder.positional_dynamic_index).max().unwrap_or(0),
74        )?;
75
76        variants
77            .into_iter()
78            .map(|(builder, inputs)| {
79                let mut bytes = function
80                    .selector()
81                    .iter()
82                    .copied()
83                    .map(|byte| SymWord::Concrete(U256::from(byte)))
84                    .collect::<Vec<_>>();
85                bytes.extend(encode_sequence(inputs.iter().map(|input| &input.value)));
86                if bytes.len() > config.max_calldata_bytes as usize {
87                    return Err(SymbolicError::Unsupported(
88                        "symbolic calldata size exceeds configured max",
89                    ));
90                }
91
92                Ok(Self { size: bytes.len(), bytes, inputs, constraints: builder.constraints })
93            })
94            .collect()
95    }
96
97    #[cfg(test)]
98    /// Implements the `load` symbolic ABI helper.
99    pub(super) fn load(&self, offset: usize) -> Result<SymWord, SymbolicError> {
100        Ok(word_from_bytes((0..32).map(|idx| self.byte(offset + idx))))
101    }
102
103    #[cfg(test)]
104    /// Implements the `byte` symbolic ABI helper.
105    pub(super) fn byte(&self, offset: usize) -> SymWord {
106        self.bytes.get(offset).cloned().unwrap_or_else(SymWord::zero)
107    }
108
109    /// Implements the `call_data` symbolic ABI helper.
110    pub(super) fn call_data(&self) -> SymCalldata {
111        SymCalldata {
112            size: self.size,
113            size_word: SymWord::Concrete(U256::from(self.size)),
114            bytes: self.bytes.clone(),
115        }
116    }
117
118    /// Returns the `model_to_args` symbolic ABI helper result.
119    pub(super) fn model_to_args(
120        &self,
121        model: &BTreeMap<String, U256>,
122    ) -> Result<Vec<DynSolValue>, SymbolicError> {
123        self.inputs.iter().map(|input| input.value.model_value(model)).collect()
124    }
125}
126
127#[derive(Clone, Debug)]
128pub(super) struct SymbolicInput {
129    pub(super) value: SymbolicAbiValue,
130}
131
132impl SymbolicInput {
133    /// Returns the `variants` symbolic ABI helper result.
134    pub(super) fn variants<'a>(
135        builder: SymbolicAbiBuilder<'a>,
136        prefix: &str,
137        idx: usize,
138        abi_name: Option<&str>,
139        ty: &str,
140    ) -> Result<Vec<(SymbolicAbiBuilder<'a>, Self)>, SymbolicError> {
141        let ty =
142            DynSolType::parse(ty).map_err(|_| SymbolicError::UnsupportedAbi(ty.to_string()))?;
143        let name = format!("{prefix}_{idx}");
144        let aliases =
145            abi_name.filter(|name| !name.is_empty()).map(str::to_string).into_iter().collect();
146        builder.value_variants(name, aliases, &ty).map(|variants| {
147            variants.into_iter().map(|(builder, value)| (builder, Self { value })).collect()
148        })
149    }
150}
151
152#[derive(Clone)]
153pub(super) struct SymbolicAbiBuilder<'a> {
154    pub(super) config: &'a SymbolicConfig,
155    pub(super) constraints: Vec<BoolExpr>,
156    pub(super) positional_dynamic_index: usize,
157}
158
159impl<'a> SymbolicAbiBuilder<'a> {
160    /// Constructs a new instance.
161    pub(super) const fn new(config: &'a SymbolicConfig) -> Self {
162        Self { config, constraints: Vec::new(), positional_dynamic_index: 0 }
163    }
164
165    /// Implements the `value` symbolic ABI helper.
166    pub(super) fn value(
167        &mut self,
168        name: String,
169        aliases: Vec<String>,
170        ty: &DynSolType,
171    ) -> Result<SymbolicAbiValue, SymbolicError> {
172        Ok(match ty {
173            DynSolType::Bool => {
174                let word = self.fresh_word(name);
175                self.constraints.push(BoolExpr::cmp(
176                    BoolExprOp::Ult,
177                    word.clone().into_expr(),
178                    Expr::Const(U256::from(2)),
179                ));
180                SymbolicAbiValue::Bool { word }
181            }
182            DynSolType::Uint(bits) => {
183                let word = self.fresh_word(name);
184                self.constrain_uint(&word, *bits);
185                SymbolicAbiValue::Uint { bits: *bits, word }
186            }
187            DynSolType::Int(bits) => {
188                let word = self.fresh_word(name);
189                self.constrain_int(&word, *bits);
190                SymbolicAbiValue::Int { bits: *bits, word }
191            }
192            DynSolType::FixedBytes(size) => SymbolicAbiValue::FixedBytes {
193                bytes: (0..*size)
194                    .map(|idx| self.fresh_byte(format!("{name}_{idx}"), false))
195                    .collect(),
196                size: *size,
197            },
198            DynSolType::Address => {
199                let word = self.fresh_word(name);
200                self.constrain_uint(&word, 160);
201                SymbolicAbiValue::Address { word }
202            }
203            DynSolType::Function => {
204                return Err(SymbolicError::UnsupportedAbi("function".to_string()));
205            }
206            DynSolType::Bytes => {
207                let len = self.next_dynamic_length(&name, &aliases, DynamicKind::Bytes)?;
208                SymbolicAbiValue::Bytes {
209                    len: SymWord::Concrete(U256::from(len)),
210                    bytes: (0..len)
211                        .map(|idx| self.fresh_byte(format!("{name}_{idx}"), false))
212                        .collect(),
213                }
214            }
215            DynSolType::String => {
216                let len = self.next_dynamic_length(&name, &aliases, DynamicKind::String)?;
217                SymbolicAbiValue::String {
218                    bytes: (0..len)
219                        .map(|idx| self.fresh_byte(format!("{name}_{idx}"), true))
220                        .collect(),
221                }
222            }
223            DynSolType::Array(inner) => {
224                let len = self.next_dynamic_length(&name, &aliases, DynamicKind::Array)?;
225                SymbolicAbiValue::Array {
226                    elements: (0..len)
227                        .map(|idx| {
228                            self.value(format!("{name}_{idx}"), child_aliases(&aliases, idx), inner)
229                        })
230                        .collect::<Result<Vec<_>, _>>()?,
231                }
232            }
233            DynSolType::FixedArray(inner, len) => SymbolicAbiValue::FixedArray {
234                elements: (0..*len)
235                    .map(|idx| {
236                        self.value(format!("{name}_{idx}"), child_aliases(&aliases, idx), inner)
237                    })
238                    .collect::<Result<Vec<_>, _>>()?,
239            },
240            DynSolType::Tuple(types) => SymbolicAbiValue::Tuple {
241                elements: types
242                    .iter()
243                    .enumerate()
244                    .map(|(idx, ty)| {
245                        self.value(format!("{name}_{idx}"), child_aliases(&aliases, idx), ty)
246                    })
247                    .collect::<Result<Vec<_>, _>>()?,
248            },
249            DynSolType::CustomStruct { tuple, .. } => SymbolicAbiValue::Tuple {
250                elements: tuple
251                    .iter()
252                    .enumerate()
253                    .map(|(idx, ty)| {
254                        self.value(format!("{name}_{idx}"), child_aliases(&aliases, idx), ty)
255                    })
256                    .collect::<Result<Vec<_>, _>>()?,
257            },
258        })
259    }
260
261    /// Returns the `value_variants` symbolic ABI helper result.
262    pub(super) fn value_variants(
263        self,
264        name: String,
265        aliases: Vec<String>,
266        ty: &DynSolType,
267    ) -> Result<Vec<(Self, SymbolicAbiValue)>, SymbolicError> {
268        Ok(match ty {
269            DynSolType::Bytes => {
270                let mut builder = self;
271                let lengths =
272                    builder.next_dynamic_length_options(&name, &aliases, DynamicKind::Bytes)?;
273                let limit = calldata_variant_limit(builder.config);
274                let mut variants = Vec::new();
275                for len in lengths {
276                    let mut builder = builder.clone();
277                    let value = SymbolicAbiValue::Bytes {
278                        len: SymWord::Concrete(U256::from(len)),
279                        bytes: (0..len as usize)
280                            .map(|idx| builder.fresh_byte(format!("{name}_{idx}"), false))
281                            .collect(),
282                    };
283                    push_variant(&mut variants, (builder, value), limit)?;
284                }
285                variants
286            }
287            DynSolType::String => {
288                let mut builder = self;
289                let lengths =
290                    builder.next_dynamic_length_options(&name, &aliases, DynamicKind::String)?;
291                let limit = calldata_variant_limit(builder.config);
292                let mut variants = Vec::new();
293                for len in lengths {
294                    let mut builder = builder.clone();
295                    let value = SymbolicAbiValue::String {
296                        bytes: (0..len as usize)
297                            .map(|idx| builder.fresh_byte(format!("{name}_{idx}"), true))
298                            .collect(),
299                    };
300                    push_variant(&mut variants, (builder, value), limit)?;
301                }
302                variants
303            }
304            DynSolType::Array(inner) => {
305                let mut builder = self;
306                let lengths =
307                    builder.next_dynamic_length_options(&name, &aliases, DynamicKind::Array)?;
308                let limit = calldata_variant_limit(builder.config);
309                let mut variants = Vec::new();
310                for len in lengths {
311                    for (builder, elements) in builder.clone().array_elements_variants(
312                        &name,
313                        &aliases,
314                        inner,
315                        len as usize,
316                    )? {
317                        push_variant(
318                            &mut variants,
319                            (builder, SymbolicAbiValue::Array { elements }),
320                            limit,
321                        )?;
322                    }
323                }
324                variants
325            }
326            DynSolType::FixedArray(inner, len) => {
327                self.array_elements_variants(&name, &aliases, inner, *len).map(|variants| {
328                    variants
329                        .into_iter()
330                        .map(|(builder, elements)| {
331                            (builder, SymbolicAbiValue::FixedArray { elements })
332                        })
333                        .collect()
334                })?
335            }
336            DynSolType::Tuple(types) => self
337                .tuple_elements_variants(&name, &aliases, types)?
338                .into_iter()
339                .map(|(builder, elements)| (builder, SymbolicAbiValue::Tuple { elements }))
340                .collect(),
341            DynSolType::CustomStruct { tuple, .. } => self
342                .tuple_elements_variants(&name, &aliases, tuple)?
343                .into_iter()
344                .map(|(builder, elements)| (builder, SymbolicAbiValue::Tuple { elements }))
345                .collect(),
346            _ => {
347                let mut builder = self;
348                let value = builder.value(name, aliases, ty)?;
349                vec![(builder, value)]
350            }
351        })
352    }
353
354    /// Returns the `array_elements_variants` symbolic ABI helper result.
355    pub(super) fn array_elements_variants(
356        self,
357        name: &str,
358        aliases: &[String],
359        inner: &DynSolType,
360        len: usize,
361    ) -> Result<Vec<(Self, Vec<SymbolicAbiValue>)>, SymbolicError> {
362        let limit = calldata_variant_limit(self.config);
363        let mut variants = vec![(self, Vec::with_capacity(len))];
364        for idx in 0..len {
365            let mut next_variants = Vec::new();
366            for (builder, elements) in variants {
367                for (builder, value) in builder.value_variants(
368                    format!("{name}_{idx}"),
369                    child_aliases(aliases, idx),
370                    inner,
371                )? {
372                    let mut elements = elements.clone();
373                    elements.push(value);
374                    push_variant(&mut next_variants, (builder, elements), limit)?;
375                }
376            }
377            variants = next_variants;
378        }
379        Ok(variants)
380    }
381
382    /// Returns the `tuple_elements_variants` symbolic ABI helper result.
383    pub(super) fn tuple_elements_variants(
384        self,
385        name: &str,
386        aliases: &[String],
387        types: &[DynSolType],
388    ) -> Result<Vec<(Self, Vec<SymbolicAbiValue>)>, SymbolicError> {
389        let limit = calldata_variant_limit(self.config);
390        let mut variants = vec![(self, Vec::with_capacity(types.len()))];
391        for (idx, ty) in types.iter().enumerate() {
392            let mut next_variants = Vec::new();
393            for (builder, elements) in variants {
394                for (builder, value) in builder.value_variants(
395                    format!("{name}_{idx}"),
396                    child_aliases(aliases, idx),
397                    ty,
398                )? {
399                    let mut elements = elements.clone();
400                    elements.push(value);
401                    push_variant(&mut next_variants, (builder, elements), limit)?;
402                }
403            }
404            variants = next_variants;
405        }
406        Ok(variants)
407    }
408
409    /// Implements the `fresh_word` symbolic ABI helper.
410    pub(super) const fn fresh_word(&self, name: String) -> SymWord {
411        SymWord::Expr(Expr::Var(name))
412    }
413
414    /// Implements the `fresh_byte` symbolic ABI helper.
415    pub(super) fn fresh_byte(&mut self, name: String, printable: bool) -> SymWord {
416        let word = self.fresh_word(name);
417        self.constraints.push(BoolExpr::cmp(
418            BoolExprOp::Ult,
419            word.clone().into_expr(),
420            Expr::Const(U256::from(256)),
421        ));
422        if printable {
423            self.constraints.push(BoolExpr::cmp(
424                BoolExprOp::Uge,
425                word.clone().into_expr(),
426                Expr::Const(U256::from(0x20)),
427            ));
428            self.constraints.push(BoolExpr::cmp(
429                BoolExprOp::Ule,
430                word.clone().into_expr(),
431                Expr::Const(U256::from(0x7e)),
432            ));
433        }
434        word
435    }
436
437    /// Implements the `next_dynamic_length` symbolic ABI helper.
438    pub(super) fn next_dynamic_length(
439        &mut self,
440        name: &str,
441        aliases: &[String],
442        kind: DynamicKind,
443    ) -> Result<usize, SymbolicError> {
444        Ok(first_dynamic_length(
445            &self.next_dynamic_length_options(name, aliases, kind)?,
446            "symbolic dynamic length",
447        )? as usize)
448    }
449
450    /// Returns the `next_dynamic_length_options` symbolic ABI helper result.
451    pub(super) fn next_dynamic_length_options(
452        &mut self,
453        name: &str,
454        aliases: &[String],
455        kind: DynamicKind,
456    ) -> Result<Vec<u32>, SymbolicError> {
457        let named_lengths = std::iter::once(name)
458            .chain(aliases.iter().map(String::as_str))
459            .find_map(|name| self.config.dynamic_lengths.get(name));
460
461        let lengths = if let Some(lengths) = named_lengths {
462            lengths.clone()
463        } else if let Some(lengths) = kind.default_lengths(self.config) {
464            lengths.to_vec()
465        } else if let Some(len) =
466            self.config.array_lengths.get(self.positional_dynamic_index).copied()
467        {
468            self.positional_dynamic_index += 1;
469            vec![len]
470        } else {
471            vec![self.config.default_dynamic_length]
472        };
473
474        if lengths.is_empty() {
475            return Err(SymbolicError::UnsupportedAbi(
476                "symbolic dynamic length set must not be empty".to_string(),
477            ));
478        }
479        for len in &lengths {
480            if *len > self.config.max_dynamic_length {
481                return Err(SymbolicError::UnsupportedAbi(format!(
482                    "symbolic {} length {len} exceeds max_dynamic_length {}",
483                    kind.name(),
484                    self.config.max_dynamic_length
485                )));
486            }
487        }
488        Ok(lengths)
489    }
490
491    /// Implements the `constrain_uint` symbolic ABI helper.
492    pub(super) fn constrain_uint(&mut self, word: &SymWord, bits: usize) {
493        if bits < 256 {
494            self.constraints.push(BoolExpr::cmp(
495                BoolExprOp::Ult,
496                word.clone().into_expr(),
497                Expr::Const(U256::from(1) << bits),
498            ));
499        }
500    }
501
502    /// Implements the `constrain_int` symbolic ABI helper.
503    pub(super) fn constrain_int(&mut self, word: &SymWord, bits: usize) {
504        if bits < 256 {
505            let byte_index = U256::from(bits / 8 - 1);
506            self.constraints.push(BoolExpr::eq(
507                word.clone().into_expr(),
508                signextend_word(byte_index, word.clone()).into_expr(),
509            ));
510        }
511    }
512}
513
514/// Validates that positional ABI length config can be consumed by at least one expanded variant.
515fn validate_positional_dynamic_lengths(
516    config: &SymbolicConfig,
517    max_positional_dynamic_index: usize,
518) -> Result<(), SymbolicError> {
519    if config.array_lengths.len() > max_positional_dynamic_index {
520        return Err(SymbolicError::UnsupportedAbi(format!(
521            "symbolic.array_lengths has {} entries but ABI used at most {} positional dynamic leaves",
522            config.array_lengths.len(),
523            max_positional_dynamic_index
524        )));
525    }
526    Ok(())
527}
528
529/// Returns the maximum number of calldata variants allowed during ABI expansion.
530fn calldata_variant_limit(config: &SymbolicConfig) -> usize {
531    config.path_width().max(1) as usize
532}
533
534/// Adds one expansion variant while enforcing the configured symbolic path-width budget.
535fn push_variant<T>(variants: &mut Vec<T>, variant: T, limit: usize) -> Result<(), SymbolicError> {
536    if variants.len() >= limit {
537        return Err(SymbolicError::CalldataVariantLimit(limit));
538    }
539    variants.push(variant);
540    Ok(())
541}
542
543#[derive(Clone, Copy)]
544pub(super) enum DynamicKind {
545    Array,
546    Bytes,
547    String,
548}
549
550impl DynamicKind {
551    /// Returns the `name` symbolic ABI helper result.
552    pub(super) const fn name(self) -> &'static str {
553        match self {
554            Self::Array => "array",
555            Self::Bytes => "bytes",
556            Self::String => "string",
557        }
558    }
559
560    /// Returns the `default_lengths` symbolic ABI helper result.
561    pub(super) fn default_lengths(self, config: &SymbolicConfig) -> Option<&[u32]> {
562        match self {
563            Self::Array if !config.default_array_lengths.is_empty() => {
564                Some(&config.default_array_lengths)
565            }
566            Self::Bytes | Self::String if !config.default_bytes_lengths.is_empty() => {
567                Some(&config.default_bytes_lengths)
568            }
569            _ => None,
570        }
571    }
572}
573
574/// Returns the `first_dynamic_length` symbolic ABI helper result.
575pub(super) fn first_dynamic_length(lengths: &[u32], field: &str) -> Result<u32, SymbolicError> {
576    lengths
577        .first()
578        .copied()
579        .ok_or_else(|| SymbolicError::UnsupportedAbi(format!("{field} must not be empty")))
580}
581
582/// Returns the `child_aliases` symbolic ABI helper result.
583pub(super) fn child_aliases(aliases: &[String], idx: usize) -> Vec<String> {
584    aliases.iter().map(|alias| format!("{alias}_{idx}")).collect()
585}
586
587#[derive(Clone, Debug)]
588pub(super) enum SymbolicAbiValue {
589    Bool { word: SymWord },
590    Uint { bits: usize, word: SymWord },
591    Int { bits: usize, word: SymWord },
592    FixedBytes { bytes: Vec<SymWord>, size: usize },
593    Address { word: SymWord },
594    Bytes { len: SymWord, bytes: Vec<SymWord> },
595    String { bytes: Vec<SymWord> },
596    Array { elements: Vec<Self> },
597    FixedArray { elements: Vec<Self> },
598    Tuple { elements: Vec<Self> },
599}
600
601impl SymbolicAbiValue {
602    /// Returns whether `is_dynamic` holds.
603    pub(super) fn is_dynamic(&self) -> bool {
604        match self {
605            Self::Bool { .. }
606            | Self::Uint { .. }
607            | Self::Int { .. }
608            | Self::FixedBytes { .. }
609            | Self::Address { .. } => false,
610            Self::Bytes { .. } | Self::String { .. } | Self::Array { .. } => true,
611            Self::FixedArray { elements } | Self::Tuple { elements } => {
612                elements.iter().any(Self::is_dynamic)
613            }
614        }
615    }
616
617    /// Implements the `head_size` symbolic ABI helper.
618    pub(super) fn head_size(&self) -> usize {
619        if self.is_dynamic() {
620            32
621        } else {
622            match self {
623                Self::Bool { .. }
624                | Self::Uint { .. }
625                | Self::Int { .. }
626                | Self::FixedBytes { .. }
627                | Self::Address { .. } => 32,
628                Self::FixedArray { elements } | Self::Tuple { elements } => {
629                    elements.iter().map(Self::head_size).sum()
630                }
631                Self::Bytes { .. } | Self::String { .. } | Self::Array { .. } => 32,
632            }
633        }
634    }
635
636    /// Implements the `encode_static` symbolic ABI helper.
637    pub(super) fn encode_static(&self) -> Vec<SymWord> {
638        match self {
639            Self::Bool { word }
640            | Self::Uint { word, .. }
641            | Self::Int { word, .. }
642            | Self::Address { word } => word_bytes(word.clone()),
643            Self::FixedBytes { bytes, .. } => {
644                let mut out = bytes.clone();
645                out.resize(32, SymWord::zero());
646                out
647            }
648            Self::FixedArray { elements } | Self::Tuple { elements } => {
649                encode_sequence(elements.iter())
650            }
651            Self::Bytes { .. } | Self::String { .. } | Self::Array { .. } => {
652                unreachable!("dynamic ABI value encoded as static")
653            }
654        }
655    }
656
657    /// Implements the `encode_dynamic_body` symbolic ABI helper.
658    pub(super) fn encode_dynamic_body(&self) -> Vec<SymWord> {
659        match self {
660            Self::Bytes { len, bytes } => encode_packed_bytes_with_len(len.clone(), bytes),
661            Self::String { bytes } => {
662                encode_packed_bytes_with_len(SymWord::Concrete(U256::from(bytes.len())), bytes)
663            }
664            Self::Array { elements } => {
665                let mut out = word_bytes(SymWord::Concrete(U256::from(elements.len())));
666                out.extend(encode_sequence(elements.iter()));
667                out
668            }
669            Self::FixedArray { elements } | Self::Tuple { elements } => {
670                encode_sequence(elements.iter())
671            }
672            Self::Bool { .. }
673            | Self::Uint { .. }
674            | Self::Int { .. }
675            | Self::FixedBytes { .. }
676            | Self::Address { .. } => unreachable!("static ABI value encoded as dynamic"),
677        }
678    }
679
680    /// Returns the `model_value` symbolic ABI helper result.
681    pub(super) fn model_value(
682        &self,
683        model: &BTreeMap<String, U256>,
684    ) -> Result<DynSolValue, SymbolicError> {
685        Ok(match self {
686            Self::Bool { word } => DynSolValue::Bool(!model_word(word, model)?.is_zero()),
687            Self::Uint { bits, word } => {
688                DynSolValue::Uint(mask_bits(model_word(word, model)?, *bits), *bits)
689            }
690            Self::Int { bits, word } => {
691                DynSolValue::Int(I256::from_raw(model_word(word, model)?), *bits)
692            }
693            Self::FixedBytes { bytes, size } => {
694                let mut word = [0u8; 32];
695                for (idx, byte) in bytes.iter().enumerate() {
696                    word[idx] = model_word(byte, model)?.to::<u8>();
697                }
698                DynSolValue::FixedBytes(B256::from(word), *size)
699            }
700            Self::Address { word } => {
701                DynSolValue::Address(word_to_address(model_word(word, model)?))
702            }
703            Self::Bytes { len, bytes } => {
704                let len = model_word(len, model)?;
705                let len = u256_to_usize(len)
706                    .filter(|len| *len <= bytes.len())
707                    .ok_or_else(|| SymbolicError::Solver("invalid symbolic bytes length".into()))?;
708                let mut bytes = model_bytes(bytes, model)?;
709                bytes.truncate(len);
710                DynSolValue::Bytes(bytes)
711            }
712            Self::String { bytes } => {
713                let bytes = model_bytes(bytes, model)?;
714                let value = String::from_utf8(bytes).map_err(|err| {
715                    SymbolicError::Solver(format!("invalid symbolic string model: {err}"))
716                })?;
717                DynSolValue::String(value)
718            }
719            Self::Array { elements } => DynSolValue::Array(
720                elements
721                    .iter()
722                    .map(|value| value.model_value(model))
723                    .collect::<Result<Vec<_>, _>>()?,
724            ),
725            Self::FixedArray { elements } => DynSolValue::FixedArray(
726                elements
727                    .iter()
728                    .map(|value| value.model_value(model))
729                    .collect::<Result<Vec<_>, _>>()?,
730            ),
731            Self::Tuple { elements } => DynSolValue::Tuple(
732                elements
733                    .iter()
734                    .map(|value| value.model_value(model))
735                    .collect::<Result<Vec<_>, _>>()?,
736            ),
737        })
738    }
739}
740
741/// Implements the `encode_sequence` symbolic ABI helper.
742pub(super) fn encode_sequence<'a>(
743    values: impl IntoIterator<Item = &'a SymbolicAbiValue>,
744) -> Vec<SymWord> {
745    let values = values.into_iter().collect::<Vec<_>>();
746    let head_size = values.iter().map(|value| value.head_size()).sum::<usize>();
747    let mut head = Vec::with_capacity(head_size);
748    let mut tail = Vec::new();
749
750    for value in values {
751        if value.is_dynamic() {
752            head.extend(word_bytes(SymWord::Concrete(U256::from(head_size + tail.len()))));
753            tail.extend(value.encode_dynamic_body());
754        } else {
755            head.extend(value.encode_static());
756        }
757    }
758
759    head.extend(tail);
760    head
761}
762
763/// Implements the `encode_packed_bytes_with_len` symbolic ABI helper.
764pub(super) fn encode_packed_bytes_with_len(len: SymWord, bytes: &[SymWord]) -> Vec<SymWord> {
765    let mut out = word_bytes(len);
766    out.extend(bytes.iter().cloned());
767    out.resize(32 + bytes.len().next_multiple_of(32), SymWord::zero());
768    out
769}