Skip to main content

foundry_evm_symbolic/executor/
cheatcodes.rs

1use foundry_cheatcodes_spec::Vm::*;
2use foundry_evm::{
3    core::backend::GLOBAL_FAIL_SLOT, inspectors::cheatcodes::current_execution_context,
4};
5
6use super::*;
7
8impl SymbolicExecutor {
9    pub(super) fn handle_assertion(
10        &mut self,
11        state: &mut PathState,
12        pass: SymBoolExpr,
13    ) -> Result<CheatcodeOutcome, SymbolicError> {
14        let fail = pass.clone().not(&mut self.cx);
15        match fail.as_const() {
16            Some(true) => return Ok(CheatcodeOutcome::Failure),
17            Some(false) => return Ok(CheatcodeOutcome::Continue(Vec::new())),
18            None => {}
19        }
20
21        let mut fail_constraints = state.constraints.clone();
22        fail_constraints.push(fail);
23        if self.is_sat_with_state(state, &fail_constraints)? {
24            state.constraints = fail_constraints;
25            return Ok(CheatcodeOutcome::Failure);
26        }
27
28        state.constraints.push(pass);
29        Ok(CheatcodeOutcome::Continue(Vec::new()))
30    }
31
32    pub(super) fn handle_full_word_array_assertion(
33        &mut self,
34        state: &mut PathState,
35        selector: [u8; 4],
36        input_offset: &SymExpr,
37        input_size: &SymExpr,
38        maximum_input_size: usize,
39    ) -> Result<CheatcodeOutcome, SymbolicError> {
40        const HEAD_SIZE: usize = 4 + 2 * 32;
41        if maximum_input_size < HEAD_SIZE {
42            return Err(SymbolicError::Unsupported("short symbolic array assertion CALL"));
43        }
44
45        let minimum_offset = state.lower_bound_usize(input_offset);
46        let maximum_offset = state.upper_bound_usize(&mut self.cx, input_offset);
47        let mut required_size = HEAD_SIZE;
48        let mut layouts = [(0usize, 0usize); 2];
49
50        for (index, layout) in layouts.iter_mut().enumerate() {
51            let head_offset = 4 + index * 32;
52            let offset = state.memory.load_word_offset_with_bounds(
53                &mut self.cx,
54                input_offset,
55                head_offset,
56                minimum_offset,
57                maximum_offset,
58            );
59            let offset = self
60                .constrained_word_with_solver(state, &offset)?
61                .and_then(|offset| usize::try_from(offset).ok())
62                .ok_or(SymbolicError::Unsupported("symbolic array assertion offset"))?;
63
64            let length_offset = 4usize
65                .checked_add(offset)
66                .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?;
67            let elements_offset = length_offset
68                .checked_add(32)
69                .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?;
70            if elements_offset > maximum_input_size {
71                return Err(SymbolicError::Unsupported("short symbolic array assertion CALL"));
72            }
73            let length = state.memory.load_word_offset_with_bounds(
74                &mut self.cx,
75                input_offset,
76                length_offset,
77                minimum_offset,
78                maximum_offset,
79            );
80            let length = self
81                .constrained_word_with_solver(state, &length)?
82                .and_then(|length| usize::try_from(length).ok())
83                .ok_or(SymbolicError::Unsupported("symbolic array assertion length"))?;
84            let byte_length = length
85                .checked_mul(32)
86                .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?;
87            let end = elements_offset
88                .checked_add(byte_length)
89                .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?;
90            if end > maximum_input_size {
91                return Err(SymbolicError::Unsupported("short symbolic array assertion CALL"));
92            }
93            required_size = required_size.max(end);
94            *layout = (elements_offset, length);
95        }
96
97        if !self.proves_expr_at_least(state, input_size, required_size)? {
98            return Err(SymbolicError::Unsupported("symbolic array assertion CALL input size"));
99        }
100
101        let [(left_offset, left_len), (right_offset, right_len)] = layouts;
102        let mut condition = if left_len == right_len {
103            let mut equal = Vec::with_capacity(left_len);
104            for element in 0..left_len {
105                let offset = element
106                    .checked_mul(32)
107                    .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?;
108                let left = state.memory.load_word_offset_with_bounds(
109                    &mut self.cx,
110                    input_offset,
111                    left_offset
112                        .checked_add(offset)
113                        .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?,
114                    minimum_offset,
115                    maximum_offset,
116                );
117                let right = state.memory.load_word_offset_with_bounds(
118                    &mut self.cx,
119                    input_offset,
120                    right_offset
121                        .checked_add(offset)
122                        .ok_or(SymbolicError::Unsupported("symbolic array assertion ABI decode"))?,
123                    minimum_offset,
124                    maximum_offset,
125                );
126                equal.push(SymBoolExpr::eq(&mut self.cx, left, right));
127            }
128            SymBoolExpr::and(&mut self.cx, equal)
129        } else {
130            SymBoolExpr::constant(&mut self.cx, false)
131        };
132        if matches!(
133            selector,
134            assertNotEq_16Call::SELECTOR
135                | assertNotEq_18Call::SELECTOR
136                | assertNotEq_22Call::SELECTOR
137        ) {
138            condition = condition.not(&mut self.cx);
139        }
140        self.handle_assertion(state, condition)
141    }
142
143    pub(super) fn set_expected_revert(
144        &mut self,
145        state: &mut PathState,
146        data: ExpectedRevertData,
147        reverter: Option<SymExpr>,
148        remaining: u64,
149    ) -> CheatcodeOutcome {
150        if state.expected_revert.is_some() {
151            return CheatcodeOutcome::Revert(error_string_return_data(
152                &mut self.cx,
153                "you must call another function prior to expecting a second revert",
154            ));
155        }
156        state.expected_revert = Some(ExpectedRevert::new(data, reverter, remaining));
157        CheatcodeOutcome::Continue(Vec::new())
158    }
159
160    fn expect_emit_from_args(
161        &mut self,
162        state: &mut PathState,
163        args_offset: usize,
164        checks: ExpectedEmitChecks,
165        emitter_arg: Option<usize>,
166        count_arg: Option<usize>,
167    ) -> Result<CheatcodeOutcome, SymbolicError> {
168        let emitter = emitter_arg
169            .map(|index| read_abi_word_arg(&mut self.cx, &state.memory, args_offset, index))
170            .transpose()?;
171        let remaining = count_arg
172            .map(|index| {
173                read_abi_u64_arg(
174                    &mut self.cx,
175                    &state.memory,
176                    args_offset,
177                    index,
178                    "symbolic vm.expectEmit",
179                )
180            })
181            .transpose()?
182            .unwrap_or(1);
183        state.expected_emit = Some(ExpectedEmit::new(checks, emitter, remaining));
184        Ok(CheatcodeOutcome::Continue(Vec::new()))
185    }
186
187    #[expect(clippy::too_many_arguments)]
188    pub(super) fn set_expected_call(
189        &mut self,
190        state: &mut PathState,
191        callee: SymExpr,
192        value: Option<U256>,
193        gas: Option<u64>,
194        min_gas: Option<u64>,
195        data: SymBytes,
196        count: Option<u64>,
197    ) -> CheatcodeOutcome {
198        let expected = ExpectedCall::new(callee, value, gas, min_gas, data, count);
199        match register_expected_call(&mut state.expected_calls, &mut self.cx, expected) {
200            Ok(()) => CheatcodeOutcome::Continue(Vec::new()),
201            Err(message) => {
202                CheatcodeOutcome::Revert(error_string_return_data(&mut self.cx, message))
203            }
204        }
205    }
206
207    pub(super) fn set_expected_create(
208        &mut self,
209        state: &mut PathState,
210        bytecode: Vec<u8>,
211        deployer: SymExpr,
212        kind: CreateKind,
213    ) -> CheatcodeOutcome {
214        state.expected_creates.push(ExpectedCreate::new(bytecode, deployer, kind));
215        CheatcodeOutcome::Continue(Vec::new())
216    }
217
218    #[expect(clippy::too_many_arguments)]
219    pub(super) fn deploy_code_cheatcode_if_needed(
220        &mut self,
221        executor: &ExecutorView<'_>,
222        state: &mut PathState,
223        worklist: &mut VecDeque<PathState>,
224        completed_paths: &mut usize,
225        selector: [u8; 4],
226        in_offset: usize,
227        out_offset: SymExpr,
228        out_size: &BoundedCopySize,
229    ) -> Result<Option<StepOutcome>, SymbolicError> {
230        let args_offset = in_offset + 4;
231        let (artifact, constructor_args) = if selector == deployCode_0Call::SELECTOR {
232            let artifact = read_abi_string_arg(
233                &mut self.cx,
234                &state.memory,
235                args_offset,
236                0,
237                "symbolic vm.deployCode",
238            )?;
239            (artifact, Vec::new())
240        } else if selector == deployCode_1Call::SELECTOR {
241            let artifact = read_abi_string_arg(
242                &mut self.cx,
243                &state.memory,
244                args_offset,
245                0,
246                "symbolic vm.deployCode",
247            )?;
248            let args = read_abi_dynamic_bytes_arg(
249                &mut self.cx,
250                &state.memory,
251                args_offset,
252                1,
253                "symbolic vm.deployCode args",
254            )?;
255            (artifact, args)
256        } else {
257            return Ok(None);
258        };
259
260        self.deploy_code_cheatcode_call(
261            executor,
262            state,
263            worklist,
264            completed_paths,
265            artifact,
266            constructor_args,
267            out_offset,
268            out_size,
269        )
270        .map(Some)
271    }
272
273    #[expect(clippy::too_many_arguments)]
274    pub(super) fn deploy_code_cheatcode_call(
275        &mut self,
276        executor: &ExecutorView<'_>,
277        state: &mut PathState,
278        worklist: &mut VecDeque<PathState>,
279        completed_paths: &mut usize,
280        artifact: String,
281        constructor_args: Vec<u8>,
282        out_offset: SymExpr,
283        out_size: &BoundedCopySize,
284    ) -> Result<StepOutcome, SymbolicError> {
285        if state.is_static {
286            state.return_data = SymReturnData::empty(&mut self.cx);
287            return Ok(StepOutcome::Revert);
288        }
289
290        self.stateless_retry_safe = false;
291        let mut initcode = artifact_code(&artifact, false)?;
292        initcode.extend_from_slice(&constructor_args);
293        let initcode = SymCode::concrete(&mut self.cx, initcode);
294
295        let nonce = state.world.nonce(executor, state.address)?;
296        let created = state.address.create(nonce);
297        let created_word = SymExpr::constant(&mut self.cx, address_word(created));
298
299        let mut failure_world = state.world.clone();
300        failure_world.increment_nonce(executor, state.address)?;
301        if failure_world.has_code_or_nonce(&mut self.cx, executor, created)? {
302            state.world = failure_world;
303            let zero = SymExpr::zero(&mut self.cx);
304            let return_data = SymReturnData::from_words(&mut self.cx, vec![zero]);
305            complete_cheatcode_call(&mut self.cx, state, out_offset, out_size, return_data)?;
306            return Ok(StepOutcome::Continue);
307        }
308
309        let zero = SymExpr::zero(&mut self.cx);
310        let calldata = SymBytes::empty(&mut self.cx);
311        let calldata = SymCalldata::from_bytes(&mut self.cx, calldata);
312        let mut frame =
313            CallFrame::new(&mut self.cx, created, created, state.address, zero, false, calldata);
314        frame.address_word = created_word.clone();
315        frame.caller_word = state.address_word.clone();
316        // Isolation runs a `deployCode` from the test frame as its own transaction.
317        let isolated = state.starts_isolated_transaction(executor);
318        let mut child = state.child(frame);
319        let pending_expected_creates = std::mem::take(&mut child.expected_creates);
320        child.world = failure_world.clone();
321        if isolated {
322            child.world.clear_transient_storage();
323        }
324        child.world.mark_current_transaction_created(created);
325        child.world.set_nonce(created, 1);
326
327        let outcomes = self.execute_external_call(executor, child, &initcode, completed_paths)?;
328        if outcomes.is_empty() {
329            return Ok(StepOutcome::AssumeRejected);
330        }
331
332        let mut parents = VecDeque::with_capacity(outcomes.len());
333        for outcome in outcomes {
334            match self.join_call_outcome(executor, state, outcome, created)? {
335                JoinedCallOutcome::Rejected => {}
336                JoinedCallOutcome::Failure(parent)
337                | JoinedCallOutcome::Success { parent, unmet_expected_revert: true, .. } => {
338                    *state = parent;
339                    return Ok(StepOutcome::Failure);
340                }
341                JoinedCallOutcome::ExceptionalHalt(mut parent) => {
342                    parent.world = failure_world.clone();
343                    parent.return_data = SymReturnData::empty(&mut self.cx);
344                    parent.copy_call_output_offset(&mut self.cx, out_offset.clone(), out_size)?;
345                    parent.stack.push(SymExpr::zero(&mut self.cx))?;
346                    parents.push_back(parent);
347                }
348                JoinedCallOutcome::ExpectedRevert { mut parent, .. } => {
349                    parent.expected_creates = pending_expected_creates.clone();
350                    parent.world = failure_world.clone();
351                    let zero = SymExpr::zero(&mut self.cx);
352                    let return_data = SymReturnData::from_words(&mut self.cx, vec![zero]);
353                    complete_cheatcode_call(
354                        &mut self.cx,
355                        &mut parent,
356                        out_offset.clone(),
357                        out_size,
358                        return_data,
359                    )?;
360                    parents.push_back(parent);
361                }
362                JoinedCallOutcome::Success { mut parent, child, .. } => {
363                    parent.world = child.world;
364                    if isolated {
365                        parent.world.restore_transient_storage(&failure_world);
366                    }
367                    parent.expected_emit = child.expected_emit;
368                    parent.expected_creates = pending_expected_creates.clone();
369                    self.observe_expected_create(
370                        &mut parent,
371                        state.address,
372                        CreateKind::Create,
373                        &child.frame.return_data,
374                    )?;
375                    if !parent.world.is_destroyed(created) {
376                        parent
377                            .world
378                            .install_code(created, child.frame.return_data.to_code(&mut self.cx)?);
379                        parent.world.set_nonce(created, 1);
380                    }
381                    let return_data =
382                        SymReturnData::from_words(&mut self.cx, vec![created_word.clone()]);
383                    complete_cheatcode_call(
384                        &mut self.cx,
385                        &mut parent,
386                        out_offset.clone(),
387                        out_size,
388                        return_data,
389                    )?;
390                    parents.push_back(parent);
391                }
392                JoinedCallOutcome::Revert { mut parent, child } => {
393                    parent.world = failure_world.clone();
394                    parent.return_data = child.frame.return_data;
395                    parent.copy_call_output_offset(&mut self.cx, out_offset.clone(), out_size)?;
396                    parent.stack.push(SymExpr::zero(&mut self.cx))?;
397                    parents.push_back(parent);
398                }
399            }
400        }
401
402        Ok(self.resume_parent_paths(state, worklist, parents))
403    }
404
405    pub(super) fn observe_expected_create(
406        &mut self,
407        state: &mut PathState,
408        deployer: Address,
409        kind: CreateKind,
410        runtime: &SymReturnData,
411    ) -> Result<(), SymbolicError> {
412        if state.expected_creates.is_empty() {
413            return Ok(());
414        }
415        let bytecode = runtime.read_concrete(&mut self.cx, "symbolic expected create bytecode")?;
416        let mut mismatch_constraints = None;
417        for idx in 0..state.expected_creates.len() {
418            let Some(condition) = state.expected_creates[idx].match_condition(
419                &mut self.cx,
420                deployer,
421                kind,
422                &bytecode,
423            ) else {
424                continue;
425            };
426            let (match_constraints, match_sat) =
427                self.constraints_with_condition(state, condition.clone())?;
428            let mismatch_condition = condition.not(&mut self.cx);
429            let (candidate_mismatch_constraints, mismatch_sat) =
430                self.constraints_with_condition(state, mismatch_condition)?;
431
432            if match_sat && !mismatch_sat {
433                state.constraints = match_constraints;
434                state.expected_creates.swap_remove(idx);
435                return Ok(());
436            }
437
438            if mismatch_sat {
439                mismatch_constraints.get_or_insert(candidate_mismatch_constraints);
440            }
441        }
442
443        if let Some(constraints) = mismatch_constraints {
444            state.constraints = constraints;
445        }
446        Ok(())
447    }
448
449    pub(super) fn branch_accesses_cheatcode_if_needed(
450        &mut self,
451        state: &mut PathState,
452        worklist: &mut VecDeque<PathState>,
453        selector: [u8; 4],
454        in_offset: usize,
455        out_offset: SymExpr,
456        out_size: &BoundedCopySize,
457    ) -> Result<Option<StepOutcome>, SymbolicError> {
458        if selector != accessesCall::SELECTOR {
459            return Ok(None);
460        }
461
462        let Some(record) = state.access_record.clone() else {
463            return Ok(None);
464        };
465        let target = read_abi_word_arg(&mut self.cx, &state.memory, in_offset + 4, 0)?;
466        if target.as_const().is_some() {
467            return Ok(None);
468        }
469
470        let addresses = record.addresses();
471        if addresses.is_empty() {
472            return Ok(None);
473        }
474
475        let mut branches = VecDeque::new();
476        let mut matched_conditions = Vec::new();
477        for address in addresses {
478            let condition = target.address_match_condition(&mut self.cx, address);
479            matched_conditions.push(condition.clone());
480            if let Some(constraints) = self.constraints_for_condition(state, condition)? {
481                let mut branch = state.clone();
482                branch.constraints = constraints;
483                let return_data = accesses_return_data(&mut self.cx, Some(&record), address);
484                complete_cheatcode_call(
485                    &mut self.cx,
486                    &mut branch,
487                    out_offset.clone(),
488                    out_size,
489                    return_data,
490                )?;
491                branches.push_back(branch);
492            }
493        }
494
495        let unmatched_conditions =
496            matched_conditions.into_iter().map(|condition| condition.not(&mut self.cx)).collect();
497        let unmatched_condition = SymBoolExpr::and(&mut self.cx, unmatched_conditions);
498        if let Some(constraints) = self.constraints_for_condition(state, unmatched_condition)? {
499            let mut branch = state.clone();
500            branch.constraints = constraints;
501            let return_data = accesses_return_data(&mut self.cx, Some(&record), Address::ZERO);
502            complete_cheatcode_call(&mut self.cx, &mut branch, out_offset, out_size, return_data)?;
503            branches.push_back(branch);
504        }
505
506        let Some(first_branch) = self.pop_next_path(&mut branches) else {
507            return Ok(Some(StepOutcome::AssumeRejected));
508        };
509        *state = first_branch;
510        worklist.extend(branches);
511        Ok(Some(StepOutcome::Continue))
512    }
513
514    pub(super) fn accesses_return_data_for_target(
515        &mut self,
516        state: &mut PathState,
517        target: SymExpr,
518    ) -> Result<SymReturnData, SymbolicError> {
519        let Some(record) = state.access_record.clone() else {
520            return Ok(accesses_return_data(&mut self.cx, None, Address::ZERO));
521        };
522
523        if let Some(target) = target.as_const() {
524            return Ok(accesses_return_data(&mut self.cx, Some(&record), word_to_address(target)));
525        }
526
527        let addresses = record.addresses();
528        if addresses.is_empty() {
529            return Ok(accesses_return_data(&mut self.cx, Some(&record), Address::ZERO));
530        }
531
532        for address in addresses {
533            let condition = target.address_match_condition(&mut self.cx, address);
534            let (match_constraints, match_sat) =
535                self.constraints_with_condition(state, condition.clone())?;
536            let mismatch_condition = condition.not(&mut self.cx);
537            let (_, mismatch_sat) = self.constraints_with_condition(state, mismatch_condition)?;
538
539            match (match_sat, mismatch_sat) {
540                (true, false) => {
541                    state.constraints = match_constraints;
542                    return Ok(accesses_return_data(&mut self.cx, Some(&record), address));
543                }
544                (true, true) => {
545                    return Err(SymbolicError::Unsupported("symbolic vm.accesses address"));
546                }
547                (false, _) => {}
548            }
549        }
550
551        Ok(accesses_return_data(&mut self.cx, Some(&record), Address::ZERO))
552    }
553
554    pub(super) fn add_call_mock(
555        &mut self,
556        state: &mut PathState,
557        callee: SymExpr,
558        value: Option<U256>,
559        data: SymBytes,
560        returns: Vec<SymReturnData>,
561        reverts: bool,
562    ) -> CheatcodeOutcome {
563        // Replace identical definitions in place to preserve mock precedence.
564        if let Some(existing) = state.call_mocks.iter_mut().find(|mock| {
565            mock.callee == callee
566                && mock.value() == value
567                && mock.data.same_bytes(&mut self.cx, &data)
568        }) {
569            *existing = CallMock::new(callee, value, data, returns, reverts);
570        } else {
571            state.call_mocks.push(CallMock::new(callee, value, data, returns, reverts));
572        }
573        CheatcodeOutcome::Continue(Vec::new())
574    }
575
576    pub(super) fn set_function_mock(
577        &mut self,
578        state: &mut PathState,
579        callee: SymExpr,
580        target: Address,
581        data: SymBytes,
582    ) -> CheatcodeOutcome {
583        if let Some(mock) = state
584            .function_mocks
585            .iter_mut()
586            .find(|mock| mock.matches_definition(&mut self.cx, &callee, &data))
587        {
588            mock.set_target(target);
589        } else {
590            state.function_mocks.push(FunctionMock::new(callee, target, data));
591        }
592        CheatcodeOutcome::Continue(Vec::new())
593    }
594
595    pub(super) fn handle_foundry_cheatcode(
596        &mut self,
597        executor: &ExecutorView<'_>,
598        state: &mut PathState,
599        selector: [u8; 4],
600        in_offset: &SymExpr,
601        input_size: &SymExpr,
602        in_size: usize,
603    ) -> Result<CheatcodeOutcome, SymbolicError> {
604        if is_full_word_array_assertion(selector) {
605            return self
606                .handle_full_word_array_assertion(state, selector, in_offset, input_size, in_size);
607        }
608        let in_offset = in_offset.as_usize_or("symbolic cheatcode CALL input offset")?;
609        let args_offset = in_offset + 4;
610        match selector {
611            assumeCall::SELECTOR => self.handle_assume(state, in_offset + 4),
612            assumeNoRevert_0Call::SELECTOR
613            | assumeNoRevert_1Call::SELECTOR
614            | assumeNoRevert_2Call::SELECTOR => {
615                if state.assume_no_revert_next_call.is_some() {
616                    return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert overlap"));
617                }
618                let filter = if selector == assumeNoRevert_0Call::SELECTOR {
619                    AssumeNoRevert::Any
620                } else {
621                    let single = selector == assumeNoRevert_1Call::SELECTOR;
622                    let mut values =
623                        decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
624                    let value = values
625                        .pop()
626                        .ok_or(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"))?;
627                    AssumeNoRevert::Filtered(if single {
628                        vec![dyn_potential_revert(&mut self.cx, &value)?]
629                    } else {
630                        dyn_potential_reverts(&mut self.cx, &value)?
631                    })
632                };
633                state.assume_no_revert_next_call = Some(filter);
634                Ok(CheatcodeOutcome::Continue(Vec::new()))
635            }
636            skip_0Call::SELECTOR | skip_1Call::SELECTOR => self.handle_skip(state, in_offset + 4),
637            recordLogsCall::SELECTOR => {
638                state.recorded_logs = Some(Vec::new());
639                state.recorded_logs_generation += 1;
640                Ok(CheatcodeOutcome::Continue(Vec::new()))
641            }
642            recordCall::SELECTOR => {
643                state.access_record = Some(AccessRecord::default());
644                Ok(CheatcodeOutcome::Continue(Vec::new()))
645            }
646            stopRecordCall::SELECTOR => {
647                state.access_record = None;
648                Ok(CheatcodeOutcome::Continue(Vec::new()))
649            }
650            accessesCall::SELECTOR => {
651                let target = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
652                Ok(CheatcodeOutcome::ContinueData(
653                    self.accesses_return_data_for_target(state, target)?,
654                ))
655            }
656            registerSloadHookCall::SELECTOR | registerSstoreHookCall::SELECTOR => {
657                let target = read_abi_address_arg(
658                    &mut self.cx,
659                    &state.memory,
660                    args_offset,
661                    0,
662                    "symbolic storage hook target",
663                )?;
664                let callback: [u8; 4] = state
665                    .memory
666                    .read_concrete(&mut self.cx, args_offset + 32, 4)?
667                    .try_into()
668                    .map_err(|_| SymbolicError::Unsupported("symbolic storage hook callback"))?;
669                let hook = SymbolicStorageHook {
670                    callback_target: state.address,
671                    callback_selector: callback,
672                };
673                if selector == registerSloadHookCall::SELECTOR {
674                    state.storage_load_hooks.insert(target, hook);
675                } else {
676                    if state
677                        .mapping_storage_store_hooks
678                        .keys()
679                        .any(|(address, _)| *address == target)
680                    {
681                        return Ok(CheatcodeOutcome::Revert(error_string_return_data(
682                            &mut self.cx,
683                            "cannot register raw SSTORE hook: mapping SSTORE hooks already exist for target",
684                        )));
685                    }
686                    state.storage_store_hooks.insert(target, hook);
687                }
688                Ok(CheatcodeOutcome::Continue(Vec::new()))
689            }
690            registerMappingSstoreHookCall::SELECTOR => {
691                let target = read_abi_address_arg(
692                    &mut self.cx,
693                    &state.memory,
694                    args_offset,
695                    0,
696                    "symbolic mapping storage hook target",
697                )?;
698                let root = read_abi_concrete_word_arg(
699                    &mut self.cx,
700                    &state.memory,
701                    args_offset,
702                    1,
703                    "symbolic mapping storage hook root",
704                )?;
705                let callback: [u8; 4] = state
706                    .memory
707                    .read_concrete(&mut self.cx, args_offset + 64, 4)?
708                    .try_into()
709                    .map_err(|_| {
710                        SymbolicError::Unsupported("symbolic mapping storage hook callback")
711                    })?;
712                let hook = SymbolicStorageHook {
713                    callback_target: state.address,
714                    callback_selector: callback,
715                };
716                if state.storage_store_hooks.contains_key(&target) {
717                    return Ok(CheatcodeOutcome::Revert(error_string_return_data(
718                        &mut self.cx,
719                        "cannot register mapping SSTORE hook: raw SSTORE hook already exists for target",
720                    )));
721                }
722                state.mapping_hook_keccak_preimages.retain(|(account, _), _| *account != target);
723                state.mapping_storage_store_hooks.insert((target, root), hook);
724                Ok(CheatcodeOutcome::Continue(Vec::new()))
725            }
726            getRecordedLogsCall::SELECTOR => {
727                state.recorded_logs_generation += 1;
728                let logs = state.recorded_logs.replace(Vec::new()).unwrap_or_default();
729                Ok(CheatcodeOutcome::ContinueData(recorded_logs_return_data(&mut self.cx, logs)))
730            }
731            getRecordedLogsJsonCall::SELECTOR => {
732                state.recorded_logs_generation += 1;
733                let logs = state.recorded_logs.replace(Vec::new()).unwrap_or_default();
734                Ok(CheatcodeOutcome::ContinueData(recorded_logs_json_return_data(
735                    &mut self.cx,
736                    logs,
737                )?))
738            }
739            expectRevert_0Call::SELECTOR
740            | expectRevert_1Call::SELECTOR
741            | expectRevert_2Call::SELECTOR
742            | expectRevert_3Call::SELECTOR
743            | expectRevert_4Call::SELECTOR
744            | expectRevert_5Call::SELECTOR
745            | expectRevert_6Call::SELECTOR
746            | expectRevert_7Call::SELECTOR
747            | expectRevert_8Call::SELECTOR
748            | expectRevert_9Call::SELECTOR
749            | expectRevert_10Call::SELECTOR
750            | expectRevert_11Call::SELECTOR
751            | expectPartialRevert_0Call::SELECTOR
752            | expectPartialRevert_1Call::SELECTOR => {
753                let mut data = ExpectedRevertData::Any;
754                let mut reverter = None;
755                let mut count = 1;
756                for (index, param) in vm_params(selector).into_iter().enumerate() {
757                    match param {
758                        DynSolType::FixedBytes(4) => {
759                            let selector = read_abi_bytes4_words_arg(
760                                &mut self.cx,
761                                &state.memory,
762                                args_offset,
763                                index,
764                            );
765                            data =
766                                ExpectedRevertData::Prefix(SymBytes::exprs(&mut self.cx, selector));
767                        }
768                        DynSolType::Bytes => {
769                            let bytes = read_abi_symbolic_dynamic_byte_exprs_arg(
770                                &mut self.cx,
771                                state,
772                                args_offset,
773                                index,
774                                self.config.max_calldata_bytes as usize,
775                                "symbolic vm.expectRevert",
776                            )?;
777                            data = ExpectedRevertData::Exact(SymBytes::exprs(&mut self.cx, bytes));
778                        }
779                        DynSolType::Address => {
780                            reverter = Some(read_abi_word_arg(
781                                &mut self.cx,
782                                &state.memory,
783                                args_offset,
784                                index,
785                            )?);
786                        }
787                        _ => {
788                            count = read_abi_u64_arg(
789                                &mut self.cx,
790                                &state.memory,
791                                args_offset,
792                                index,
793                                "symbolic vm.expectRevert",
794                            )?;
795                        }
796                    }
797                }
798                // Forge reads a zero count as "expect no revert", which is not modeled.
799                if count == 0 {
800                    return Err(SymbolicError::Unsupported("symbolic vm.expectRevert count 0"));
801                }
802                Ok(self.set_expected_revert(state, data, reverter, count))
803            }
804            expectEmitAnonymous_0Call::SELECTOR
805            | expectEmitAnonymous_1Call::SELECTOR
806            | expectEmitAnonymous_2Call::SELECTOR
807            | expectEmitAnonymous_3Call::SELECTOR
808            | expectEmit_0Call::SELECTOR
809            | expectEmit_1Call::SELECTOR
810            | expectEmit_2Call::SELECTOR
811            | expectEmit_3Call::SELECTOR
812            | expectEmit_4Call::SELECTOR
813            | expectEmit_5Call::SELECTOR
814            | expectEmit_6Call::SELECTOR
815            | expectEmit_7Call::SELECTOR => {
816                let params = vm_params(selector);
817                let checks = match params.iter().filter(|param| **param == DynSolType::Bool).count()
818                {
819                    0 => ExpectedEmitChecks::default(),
820                    4 => ExpectedEmitChecks::from_non_anonymous_args(
821                        &mut self.cx,
822                        &state.memory,
823                        args_offset,
824                    )?,
825                    _ => ExpectedEmitChecks::from_anonymous_args(
826                        &mut self.cx,
827                        &state.memory,
828                        args_offset,
829                    )?,
830                };
831                let emitter = params.iter().position(|param| *param == DynSolType::Address);
832                let count = params.iter().position(|param| *param == DynSolType::Uint(64));
833                self.expect_emit_from_args(state, args_offset, checks, emitter, count)
834            }
835            expectCall_0Call::SELECTOR
836            | expectCall_1Call::SELECTOR
837            | expectCall_2Call::SELECTOR
838            | expectCall_3Call::SELECTOR
839            | expectCall_4Call::SELECTOR
840            | expectCall_5Call::SELECTOR
841            | expectCallMinGas_0Call::SELECTOR
842            | expectCallMinGas_1Call::SELECTOR => {
843                let callee = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
844                let mut value = None;
845                let mut gas = None;
846                let mut data = None;
847                let mut count = None;
848                for (index, param) in vm_params(selector).into_iter().enumerate().skip(1) {
849                    match param {
850                        DynSolType::Uint(256) => {
851                            value = Some(read_abi_concrete_word_arg(
852                                &mut self.cx,
853                                &state.memory,
854                                args_offset,
855                                index,
856                                "symbolic vm.expectCall",
857                            )?);
858                        }
859                        DynSolType::Bytes => {
860                            data = Some(read_abi_symbolic_dynamic_byte_exprs_arg(
861                                &mut self.cx,
862                                state,
863                                args_offset,
864                                index,
865                                self.config.max_calldata_bytes as usize,
866                                "symbolic vm.expectCall",
867                            )?);
868                        }
869                        _ => {
870                            let arg = read_abi_u64_arg(
871                                &mut self.cx,
872                                &state.memory,
873                                args_offset,
874                                index,
875                                "symbolic vm.expectCall",
876                            )?;
877                            if data.is_some() { count = Some(arg) } else { gas = Some(arg) }
878                        }
879                    }
880                }
881                let data = SymBytes::exprs(&mut self.cx, data.unwrap_or_default());
882                let (gas, min_gas) = if matches!(
883                    selector,
884                    expectCallMinGas_0Call::SELECTOR | expectCallMinGas_1Call::SELECTOR
885                ) {
886                    (None, gas)
887                } else {
888                    (gas, None)
889                };
890                Ok(self.set_expected_call(state, callee, value, gas, min_gas, data, count))
891            }
892            expectCreateCall::SELECTOR | expectCreate2Call::SELECTOR => {
893                let bytecode = read_abi_dynamic_bytes_arg(
894                    &mut self.cx,
895                    &state.memory,
896                    args_offset,
897                    0,
898                    "symbolic vm.expectCreate bytecode",
899                )?;
900                let deployer = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
901                let kind = if selector == expectCreateCall::SELECTOR {
902                    CreateKind::Create
903                } else {
904                    CreateKind::Create2
905                };
906                Ok(self.set_expected_create(state, bytecode, deployer, kind))
907            }
908            clearMockedCallsCall::SELECTOR => {
909                state.call_mocks.clear();
910                Ok(CheatcodeOutcome::Continue(Vec::new()))
911            }
912            mockCall_0Call::SELECTOR
913            | mockCall_1Call::SELECTOR
914            | mockCall_2Call::SELECTOR
915            | mockCall_3Call::SELECTOR
916            | mockCallRevert_0Call::SELECTOR
917            | mockCallRevert_1Call::SELECTOR
918            | mockCallRevert_2Call::SELECTOR
919            | mockCallRevert_3Call::SELECTOR => {
920                let revert = matches!(
921                    selector,
922                    mockCallRevert_0Call::SELECTOR
923                        | mockCallRevert_1Call::SELECTOR
924                        | mockCallRevert_2Call::SELECTOR
925                        | mockCallRevert_3Call::SELECTOR
926                );
927                let message =
928                    if revert { "symbolic vm.mockCallRevert" } else { "symbolic vm.mockCall" };
929                let params = vm_params(selector);
930                let callee = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
931                let value = (params[1] == DynSolType::Uint(256))
932                    .then(|| {
933                        read_abi_concrete_word_arg(
934                            &mut self.cx,
935                            &state.memory,
936                            args_offset,
937                            1,
938                            message,
939                        )
940                    })
941                    .transpose()?;
942                let data_index = params.len() - 2;
943                let data = if params[data_index] == DynSolType::FixedBytes(4) {
944                    read_abi_bytes4_words_arg(&mut self.cx, &state.memory, args_offset, data_index)
945                } else {
946                    read_abi_symbolic_dynamic_byte_exprs_arg(
947                        &mut self.cx,
948                        state,
949                        args_offset,
950                        data_index,
951                        self.config.max_calldata_bytes as usize,
952                        message,
953                    )?
954                };
955                let ret = read_abi_dynamic_return_data_arg(
956                    &mut self.cx,
957                    state,
958                    args_offset,
959                    data_index + 1,
960                    self.config.max_calldata_bytes as usize,
961                    message,
962                )?;
963                let data = SymBytes::exprs(&mut self.cx, data);
964                Ok(self.add_call_mock(state, callee, value, data, vec![ret], revert))
965            }
966            mockCalls_0Call::SELECTOR | mockCalls_1Call::SELECTOR => {
967                let has_value = selector == mockCalls_1Call::SELECTOR;
968                let (value, data_idx, ret_idx) = if has_value {
969                    let value = read_abi_concrete_word_arg(
970                        &mut self.cx,
971                        &state.memory,
972                        args_offset,
973                        1,
974                        "symbolic vm.mockCalls",
975                    )?;
976                    (Some(value), 2, 3)
977                } else {
978                    (None, 1, 2)
979                };
980                let callee = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
981                let data = read_abi_symbolic_dynamic_byte_exprs_arg(
982                    &mut self.cx,
983                    state,
984                    args_offset,
985                    data_idx,
986                    self.config.max_calldata_bytes as usize,
987                    "symbolic vm.mockCalls data",
988                )?;
989                let returns = read_abi_symbolic_dynamic_bytes_array_arg(
990                    &mut self.cx,
991                    state,
992                    args_offset,
993                    ret_idx,
994                    self.config.max_dynamic_length as usize,
995                    self.config.max_calldata_bytes as usize,
996                )?;
997                let data = SymBytes::exprs(&mut self.cx, data);
998                Ok(self.add_call_mock(state, callee, value, data, returns, false))
999            }
1000            mockFunctionCall::SELECTOR => {
1001                let callee = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
1002                let target = read_abi_address_arg(
1003                    &mut self.cx,
1004                    &state.memory,
1005                    args_offset,
1006                    1,
1007                    "symbolic vm.mockFunction",
1008                )?;
1009                let data = read_abi_symbolic_dynamic_byte_exprs_arg(
1010                    &mut self.cx,
1011                    state,
1012                    args_offset,
1013                    2,
1014                    self.config.max_calldata_bytes as usize,
1015                    "symbolic vm.mockFunction",
1016                )?;
1017                let data = SymBytes::exprs(&mut self.cx, data);
1018                Ok(self.set_function_mock(state, callee, target, data))
1019            }
1020            prank_0Call::SELECTOR
1021            | prank_1Call::SELECTOR
1022            | prank_2Call::SELECTOR
1023            | prank_3Call::SELECTOR
1024            | startPrank_0Call::SELECTOR
1025            | startPrank_1Call::SELECTOR
1026            | startPrank_2Call::SELECTOR
1027            | startPrank_3Call::SELECTOR => {
1028                let persistent = matches!(
1029                    selector,
1030                    startPrank_0Call::SELECTOR
1031                        | startPrank_1Call::SELECTOR
1032                        | startPrank_2Call::SELECTOR
1033                        | startPrank_3Call::SELECTOR
1034                );
1035                let (message, delegate_message) = if persistent {
1036                    ("symbolic vm.startPrank", "symbolic vm.startPrank delegatecall")
1037                } else {
1038                    ("symbolic vm.prank", "symbolic vm.prank delegatecall")
1039                };
1040                let params = vm_params(selector);
1041                if params.last() == Some(&DynSolType::Bool)
1042                    && read_abi_bool_arg(
1043                        &mut self.cx,
1044                        &state.memory,
1045                        args_offset,
1046                        params.len() - 1,
1047                        message,
1048                    )?
1049                {
1050                    return Err(SymbolicError::Unsupported(delegate_message));
1051                }
1052                let caller = read_abi_address_word_or_symbolic_slot_arg(
1053                    &mut self.cx,
1054                    state,
1055                    args_offset,
1056                    0,
1057                )?;
1058                let origin = (params.get(1) == Some(&DynSolType::Address))
1059                    .then(|| {
1060                        read_abi_address_word_or_symbolic_slot_arg(
1061                            &mut self.cx,
1062                            state,
1063                            args_offset,
1064                            1,
1065                        )
1066                    })
1067                    .transpose()?;
1068                if persistent {
1069                    state.prank.set_persistent(caller, origin);
1070                } else {
1071                    state.prank.set_next(caller, origin);
1072                }
1073                Ok(CheatcodeOutcome::Continue(Vec::new()))
1074            }
1075            stopPrankCall::SELECTOR => {
1076                state.prank = SymbolicPrank::default();
1077                Ok(CheatcodeOutcome::Continue(Vec::new()))
1078            }
1079            readCallersCall::SELECTOR => {
1080                Ok(CheatcodeOutcome::Continue(state.read_callers_words(&mut self.cx)))
1081            }
1082            addrCall::SELECTOR => {
1083                let private_key = read_abi_constrained_word_arg(
1084                    &mut self.cx,
1085                    state,
1086                    args_offset,
1087                    0,
1088                    "symbolic vm.addr",
1089                )?;
1090                let address = private_key_address(private_key)?;
1091                let address = SymExpr::constant(&mut self.cx, address_word(address));
1092                Ok(CheatcodeOutcome::Continue(vec![address]))
1093            }
1094            sign_1Call::SELECTOR | signCompact_1Call::SELECTOR => {
1095                let compact = selector == signCompact_1Call::SELECTOR;
1096                let message = if compact { "symbolic vm.signCompact" } else { "symbolic vm.sign" };
1097                let private_key =
1098                    read_abi_constrained_word_arg(&mut self.cx, state, args_offset, 0, message)?;
1099                let digest =
1100                    read_abi_constrained_word_arg(&mut self.cx, state, args_offset, 1, message)?;
1101                Ok(CheatcodeOutcome::Continue(if compact {
1102                    sign_compact_hash_words(&mut self.cx, private_key, digest)?
1103                } else {
1104                    sign_hash_words(&mut self.cx, private_key, digest)?
1105                }))
1106            }
1107            deriveKey_0Call::SELECTOR
1108            | deriveKey_1Call::SELECTOR
1109            | deriveKey_2Call::SELECTOR
1110            | deriveKey_3Call::SELECTOR => {
1111                let params = vm_params(selector);
1112                let mnemonic = read_abi_string_arg(
1113                    &mut self.cx,
1114                    &state.memory,
1115                    args_offset,
1116                    0,
1117                    "symbolic vm.deriveKey",
1118                )?;
1119                let index_arg = if params[1] == DynSolType::String { 2 } else { 1 };
1120                let path = if index_arg == 2 {
1121                    read_abi_string_arg(
1122                        &mut self.cx,
1123                        &state.memory,
1124                        args_offset,
1125                        1,
1126                        "symbolic vm.deriveKey",
1127                    )?
1128                } else {
1129                    DEFAULT_DERIVATION_PATH_PREFIX.to_string()
1130                };
1131                let index = read_abi_u32_arg(
1132                    &mut self.cx,
1133                    &state.memory,
1134                    args_offset,
1135                    index_arg,
1136                    "symbolic vm.deriveKey",
1137                )?;
1138                let private_key = if params.len() > index_arg + 1 {
1139                    let language = read_abi_string_arg(
1140                        &mut self.cx,
1141                        &state.memory,
1142                        args_offset,
1143                        index_arg + 1,
1144                        "symbolic vm.deriveKey",
1145                    )?;
1146                    derive_private_key_with_language(&mnemonic, &path, index, &language)?
1147                } else {
1148                    derive_private_key::<English>(&mnemonic, &path, index)?
1149                };
1150                let private_key = SymExpr::constant(&mut self.cx, private_key);
1151                Ok(CheatcodeOutcome::Continue(vec![private_key]))
1152            }
1153            rememberKeyCall::SELECTOR => {
1154                let private_key = read_abi_constrained_word_arg(
1155                    &mut self.cx,
1156                    state,
1157                    args_offset,
1158                    0,
1159                    "symbolic vm.rememberKey",
1160                )?;
1161                let address = private_key_address(private_key)?;
1162                state.wallets.insert(address);
1163                let address = SymExpr::constant(&mut self.cx, address_word(address));
1164                Ok(CheatcodeOutcome::Continue(vec![address]))
1165            }
1166            rememberKeys_0Call::SELECTOR | rememberKeys_1Call::SELECTOR => {
1167                let mnemonic = read_abi_string_arg(
1168                    &mut self.cx,
1169                    &state.memory,
1170                    args_offset,
1171                    0,
1172                    "symbolic vm.rememberKeys",
1173                )?;
1174                let path = read_abi_string_arg(
1175                    &mut self.cx,
1176                    &state.memory,
1177                    args_offset,
1178                    1,
1179                    "symbolic vm.rememberKeys",
1180                )?;
1181                let (language, count_index) = if selector == rememberKeys_1Call::SELECTOR {
1182                    (
1183                        Some(read_abi_string_arg(
1184                            &mut self.cx,
1185                            &state.memory,
1186                            args_offset,
1187                            2,
1188                            "symbolic vm.rememberKeys",
1189                        )?),
1190                        3,
1191                    )
1192                } else {
1193                    (None, 2)
1194                };
1195                let count = read_abi_u32_arg(
1196                    &mut self.cx,
1197                    &state.memory,
1198                    args_offset,
1199                    count_index,
1200                    "symbolic vm.rememberKeys",
1201                )?;
1202                if count > MAX_REMEMBER_KEYS {
1203                    return Err(SymbolicError::Unsupported("symbolic vm.rememberKeys count"));
1204                }
1205                let mut addresses = Vec::with_capacity(count as usize);
1206                for index in 0..count {
1207                    let private_key = if let Some(language) = &language {
1208                        derive_private_key_with_language(&mnemonic, &path, index, language)?
1209                    } else {
1210                        derive_private_key::<English>(&mnemonic, &path, index)?
1211                    };
1212                    let address = private_key_address(private_key)?;
1213                    state.wallets.insert(address);
1214                    addresses.push(DynSolValue::Address(address));
1215                }
1216                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(
1217                    &mut self.cx,
1218                    DynSolValue::Array(addresses),
1219                )))
1220            }
1221            getWalletsCall::SELECTOR => {
1222                let wallets = DynSolValue::Array(
1223                    state.wallets.iter().copied().map(DynSolValue::Address).collect(),
1224                );
1225                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(&mut self.cx, wallets)))
1226            }
1227            storeCall::SELECTOR => {
1228                let target =
1229                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1230                let slot = state.memory.load_word(&mut self.cx, in_offset + 36)?;
1231                let value = state.memory.load_word(&mut self.cx, in_offset + 68)?;
1232                let failed_slot = SymExpr::constant(&mut self.cx, GLOBAL_FAIL_SLOT);
1233                let one = SymExpr::one(&mut self.cx);
1234                if target == CHEATCODE_ADDRESS && slot == failed_slot && value == one {
1235                    return Ok(CheatcodeOutcome::Failure);
1236                }
1237                state.world.sstore(target, slot, value);
1238                Ok(CheatcodeOutcome::Continue(Vec::new()))
1239            }
1240            loadCall::SELECTOR => {
1241                let target =
1242                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1243                let slot = state.memory.load_word(&mut self.cx, in_offset + 36)?;
1244                let concrete_slot = state.constrained_word(&mut self.cx, &slot);
1245                let value =
1246                    state.world.sload(&mut self.cx, executor, target, slot, concrete_slot)?;
1247                Ok(CheatcodeOutcome::Continue(vec![value]))
1248            }
1249            getNonce_0Call::SELECTOR => {
1250                let target =
1251                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1252                let nonce = state.world.nonce(executor, target)?;
1253                let nonce = SymExpr::constant(&mut self.cx, U256::from(nonce));
1254                Ok(CheatcodeOutcome::Continue(vec![nonce]))
1255            }
1256            computeCreateAddressCall::SELECTOR => {
1257                let deployer = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
1258                let nonce = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
1259                let address = compute_create_address_word(&mut self.cx, state, deployer, nonce)?;
1260                Ok(CheatcodeOutcome::Continue(vec![address]))
1261            }
1262            computeCreate2Address_0Call::SELECTOR | computeCreate2Address_1Call::SELECTOR => {
1263                let salt = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
1264                let init_code_hash =
1265                    read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
1266                let deployer = if selector == computeCreate2Address_0Call::SELECTOR {
1267                    read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 2)?
1268                } else {
1269                    SymExpr::constant(&mut self.cx, address_word(DEFAULT_CREATE2_DEPLOYER))
1270                };
1271                let address = compute_create2_address_word(
1272                    &mut self.cx,
1273                    state,
1274                    deployer,
1275                    salt,
1276                    init_code_hash,
1277                )?;
1278                Ok(CheatcodeOutcome::Continue(vec![address]))
1279            }
1280            etchCall::SELECTOR => {
1281                let target =
1282                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1283                let code = read_abi_symbolic_dynamic_byte_exprs_arg(
1284                    &mut self.cx,
1285                    state,
1286                    args_offset,
1287                    1,
1288                    self.config.max_dynamic_length as usize,
1289                    "symbolic vm.etch",
1290                )?;
1291                let code = SymCode::from_byte_exprs(&mut self.cx, code);
1292                let delegation_prefix = SymBytes::concrete(&mut self.cx, vec![0xef, 0x01]);
1293                let delegation = code
1294                    .read_bytes(&mut self.cx, 0, delegation_prefix.len())
1295                    .prefix_condition(&mut self.cx, &delegation_prefix);
1296                if let Some(delegation) = delegation {
1297                    let valid = if code.len() == EIP7702_BYTECODE_LEN {
1298                        let delegation = SymBytes::concrete(&mut self.cx, vec![0xef, 0x01, 0x00]);
1299                        code.read_bytes(&mut self.cx, 0, delegation.len())
1300                            .prefix_condition(&mut self.cx, &delegation)
1301                            .unwrap_or_else(|| SymBoolExpr::constant(&mut self.cx, false))
1302                    } else {
1303                        SymBoolExpr::constant(&mut self.cx, false)
1304                    };
1305                    let invalid = valid.not(&mut self.cx);
1306                    let malformed = SymBoolExpr::and(&mut self.cx, vec![delegation, invalid]);
1307                    let (_, malformed_sat) = self.constraints_with_condition(state, malformed)?;
1308                    if malformed_sat {
1309                        return Err(SymbolicError::Unsupported("symbolic vm.etch EIP-7702 code"));
1310                    }
1311                }
1312                state.world.install_code(target, code);
1313                Ok(CheatcodeOutcome::Continue(Vec::new()))
1314            }
1315            getCodeCall::SELECTOR | getDeployedCodeCall::SELECTOR => {
1316                let artifact = read_abi_string_arg(
1317                    &mut self.cx,
1318                    &state.memory,
1319                    args_offset,
1320                    0,
1321                    "symbolic vm.getCode",
1322                )?;
1323                self.stateless_retry_safe = false;
1324                let code = artifact_code(&artifact, selector == getDeployedCodeCall::SELECTOR)?;
1325                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(&mut self.cx, &code)))
1326            }
1327            dealCall::SELECTOR => {
1328                let target =
1329                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1330                let value = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
1331                if value.contains_gasleft() {
1332                    return Err(SymbolicError::Unsupported("GAS/gasleft() not modeled"));
1333                }
1334                let value = state
1335                    .constrained_word(&mut self.cx, &value)
1336                    .map(|value| SymExpr::constant(&mut self.cx, value))
1337                    .unwrap_or(value);
1338                state.world.set_balance_word(target, value);
1339                Ok(CheatcodeOutcome::Continue(Vec::new()))
1340            }
1341            setNonceCall::SELECTOR | setNonceUnsafeCall::SELECTOR => {
1342                let target =
1343                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1344                let nonce = read_abi_constrained_word_arg(
1345                    &mut self.cx,
1346                    state,
1347                    args_offset,
1348                    1,
1349                    "symbolic vm.setNonce",
1350                )?;
1351                let Ok(nonce) = u64::try_from(nonce) else {
1352                    return Err(SymbolicError::Unsupported("symbolic vm.setNonce nonce"));
1353                };
1354                if selector == setNonceCall::SELECTOR
1355                    && nonce < state.world.nonce(executor, target)?
1356                {
1357                    return Ok(CheatcodeOutcome::Failure);
1358                }
1359                state.world.set_nonce(target, nonce);
1360                Ok(CheatcodeOutcome::Continue(Vec::new()))
1361            }
1362            resetNonceCall::SELECTOR => {
1363                let target =
1364                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1365                let code = state.world.extcode(&mut self.cx, executor, target)?;
1366                // Like the concrete cheatcode, EIP-7702 delegated EOAs reset to 0.
1367                let delegated = if !code.is_legacy() && code.len() == EIP7702_BYTECODE_LEN {
1368                    let prefix = SymBytes::concrete(&mut self.cx, vec![0xef, 0x01, 0x00]);
1369                    code.read_bytes(&mut self.cx, 0, prefix.len())
1370                        .prefix_condition(&mut self.cx, &prefix)
1371                } else {
1372                    None
1373                };
1374                let is_eoa = if let Some(delegated) = delegated {
1375                    let (_, delegated_sat) =
1376                        self.constraints_with_condition(state, delegated.clone())?;
1377                    let not_delegated = delegated.not(&mut self.cx);
1378                    let (_, not_delegated_sat) =
1379                        self.constraints_with_condition(state, not_delegated)?;
1380                    if delegated_sat && not_delegated_sat {
1381                        return Err(SymbolicError::Unsupported(
1382                            "symbolic vm.resetNonce code prefix",
1383                        ));
1384                    }
1385                    delegated_sat
1386                } else {
1387                    code.is_empty()
1388                };
1389                let nonce = if is_eoa { 0 } else { 1 };
1390                state.world.set_nonce(target, nonce);
1391                Ok(CheatcodeOutcome::Continue(Vec::new()))
1392            }
1393            allowCheatcodesCall::SELECTOR => Ok(CheatcodeOutcome::Continue(Vec::new())),
1394            makePersistent_0Call::SELECTOR
1395            | makePersistent_1Call::SELECTOR
1396            | makePersistent_2Call::SELECTOR => {
1397                for index in 0..vm_params(selector).len() {
1398                    let account = read_abi_address_or_symbolic_slot_arg(
1399                        &mut self.cx,
1400                        state,
1401                        args_offset,
1402                        index,
1403                    )?;
1404                    state.persistent_accounts.insert(account);
1405                }
1406                Ok(CheatcodeOutcome::Continue(Vec::new()))
1407            }
1408            makePersistent_3Call::SELECTOR => {
1409                let values =
1410                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1411                for account in dyn_address_array(&values[0])? {
1412                    state.persistent_accounts.insert(account);
1413                }
1414                Ok(CheatcodeOutcome::Continue(Vec::new()))
1415            }
1416            revokePersistent_0Call::SELECTOR => {
1417                let account =
1418                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1419                state.persistent_accounts.remove(&account);
1420                Ok(CheatcodeOutcome::Continue(Vec::new()))
1421            }
1422            revokePersistent_1Call::SELECTOR => {
1423                let values =
1424                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1425                for account in dyn_address_array(&values[0])? {
1426                    state.persistent_accounts.remove(&account);
1427                }
1428                Ok(CheatcodeOutcome::Continue(Vec::new()))
1429            }
1430            isPersistentCall::SELECTOR => {
1431                let account =
1432                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
1433                let exists = SymExpr::constant(
1434                    &mut self.cx,
1435                    U256::from(state.persistent_accounts.contains(&account)),
1436                );
1437                Ok(CheatcodeOutcome::Continue(vec![exists]))
1438            }
1439            activeForkCall::SELECTOR => {
1440                let id = executor.active_fork_id.ok_or(SymbolicError::Unsupported(
1441                    "symbolic vm.activeFork requires an active forked executor",
1442                ))?;
1443                let id = SymExpr::constant(&mut self.cx, id);
1444                Ok(CheatcodeOutcome::Continue(vec![id]))
1445            }
1446            selectForkCall::SELECTOR => {
1447                let id = read_abi_constrained_word_arg(
1448                    &mut self.cx,
1449                    state,
1450                    args_offset,
1451                    0,
1452                    "symbolic vm.selectFork id",
1453                )?;
1454                if executor.active_fork_id == Some(id) {
1455                    return Ok(CheatcodeOutcome::Continue(Vec::new()));
1456                }
1457                Err(SymbolicError::Unsupported(
1458                    "symbolic vm.selectFork can only select the already active fork",
1459                ))
1460            }
1461            rollFork_0Call::SELECTOR | rollFork_2Call::SELECTOR => {
1462                let with_id = selector == rollFork_2Call::SELECTOR;
1463                let active_fork = !with_id
1464                    || executor.active_fork_id
1465                        == Some(read_abi_constrained_word_arg(
1466                            &mut self.cx,
1467                            state,
1468                            args_offset,
1469                            0,
1470                            "symbolic vm.rollFork id",
1471                        )?);
1472                let block_number = read_abi_constrained_word_arg(
1473                    &mut self.cx,
1474                    state,
1475                    args_offset,
1476                    usize::from(with_id),
1477                    "symbolic vm.rollFork block number",
1478                )?;
1479                let current =
1480                    state.block.number.as_const_or("symbolic vm.rollFork current block")?;
1481                if active_fork && block_number == current {
1482                    return Ok(CheatcodeOutcome::Continue(Vec::new()));
1483                }
1484                Err(SymbolicError::Unsupported(
1485                    "symbolic vm.rollFork cannot change the active fork block during symbolic execution",
1486                ))
1487            }
1488            createFork_0Call::SELECTOR
1489            | createFork_1Call::SELECTOR
1490            | createFork_2Call::SELECTOR
1491            | createSelectFork_0Call::SELECTOR
1492            | createSelectFork_1Call::SELECTOR
1493            | createSelectFork_2Call::SELECTOR
1494            | rollFork_1Call::SELECTOR
1495            | rollFork_3Call::SELECTOR => Err(SymbolicError::Unsupported(
1496                "symbolic fork creation and fork block mutation must happen before symbolic execution",
1497            )),
1498            snapshotCall::SELECTOR | snapshotStateCall::SELECTOR => {
1499                let id = state.world.snapshot_state();
1500                let id = SymExpr::constant(&mut self.cx, id);
1501                Ok(CheatcodeOutcome::Continue(vec![id]))
1502            }
1503            revertToCall::SELECTOR
1504            | revertToStateCall::SELECTOR
1505            | revertToAndDeleteCall::SELECTOR
1506            | revertToStateAndDeleteCall::SELECTOR => {
1507                let id = read_abi_constrained_word_arg(
1508                    &mut self.cx,
1509                    state,
1510                    args_offset,
1511                    0,
1512                    "symbolic vm.revertToState snapshot",
1513                )?;
1514                let success = state.world.restore_snapshot(id);
1515                if success
1516                    && (selector == revertToAndDeleteCall::SELECTOR
1517                        || selector == revertToStateAndDeleteCall::SELECTOR)
1518                {
1519                    state.world.delete_snapshot(id);
1520                }
1521                let success = SymExpr::constant(&mut self.cx, U256::from(success));
1522                Ok(CheatcodeOutcome::Continue(vec![success]))
1523            }
1524            deleteSnapshotCall::SELECTOR | deleteStateSnapshotCall::SELECTOR => {
1525                let id = read_abi_constrained_word_arg(
1526                    &mut self.cx,
1527                    state,
1528                    args_offset,
1529                    0,
1530                    "symbolic vm.deleteStateSnapshot snapshot",
1531                )?;
1532                let success = state.world.delete_snapshot(id);
1533                let success = SymExpr::constant(&mut self.cx, U256::from(success));
1534                Ok(CheatcodeOutcome::Continue(vec![success]))
1535            }
1536            deleteSnapshotsCall::SELECTOR | deleteStateSnapshotsCall::SELECTOR => {
1537                state.world.delete_snapshots();
1538                Ok(CheatcodeOutcome::Continue(Vec::new()))
1539            }
1540            warpCall::SELECTOR => {
1541                state.block.timestamp = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1542                Ok(CheatcodeOutcome::Continue(Vec::new()))
1543            }
1544            rollCall::SELECTOR => {
1545                state.block.number = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1546                Ok(CheatcodeOutcome::Continue(Vec::new()))
1547            }
1548            setBlockhashCall::SELECTOR => {
1549                let block_number = read_abi_constrained_word_arg(
1550                    &mut self.cx,
1551                    state,
1552                    args_offset,
1553                    0,
1554                    "symbolic vm.setBlockhash block number",
1555                )?;
1556                let block_hash = state.memory.load_word(&mut self.cx, in_offset + 36)?;
1557                state.block.set_block_hash(block_number, block_hash)?;
1558                Ok(CheatcodeOutcome::Continue(Vec::new()))
1559            }
1560            prevrandao_0Call::SELECTOR | prevrandao_1Call::SELECTOR => {
1561                state.block.difficulty = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1562                Ok(CheatcodeOutcome::Continue(Vec::new()))
1563            }
1564            blobhashesCall::SELECTOR => {
1565                let values =
1566                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1567                state.block.blob_hashes = dyn_bytes32_array(&values[0])?;
1568                Ok(CheatcodeOutcome::Continue(Vec::new()))
1569            }
1570            getBlobhashesCall::SELECTOR => {
1571                let value = DynSolValue::Array(
1572                    state
1573                        .block
1574                        .blob_hashes
1575                        .iter()
1576                        .copied()
1577                        .map(|hash| DynSolValue::FixedBytes(hash, 32))
1578                        .collect(),
1579                );
1580                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(&mut self.cx, value)))
1581            }
1582            feeCall::SELECTOR => {
1583                state.block.basefee = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1584                Ok(CheatcodeOutcome::Continue(Vec::new()))
1585            }
1586            blobBaseFeeCall::SELECTOR => {
1587                state.block.blob_basefee = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1588                Ok(CheatcodeOutcome::Continue(Vec::new()))
1589            }
1590            getBlobBaseFeeCall::SELECTOR => {
1591                Ok(CheatcodeOutcome::Continue(vec![state.block.blob_basefee.clone()]))
1592            }
1593            chainIdCall::SELECTOR => {
1594                state.block.chain_id = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1595                Ok(CheatcodeOutcome::Continue(Vec::new()))
1596            }
1597            getChainIdCall::SELECTOR => {
1598                Ok(CheatcodeOutcome::Continue(vec![state.block.chain_id.clone()]))
1599            }
1600            difficultyCall::SELECTOR => {
1601                state.block.difficulty = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1602                Ok(CheatcodeOutcome::Continue(Vec::new()))
1603            }
1604            coinbaseCall::SELECTOR => {
1605                let coinbase = read_abi_constrained_address_arg(
1606                    &mut self.cx,
1607                    state,
1608                    args_offset,
1609                    0,
1610                    "symbolic vm.coinbase value",
1611                )?;
1612                state.block.coinbase = coinbase;
1613                Ok(CheatcodeOutcome::Continue(Vec::new()))
1614            }
1615            getBlockNumberCall::SELECTOR => {
1616                Ok(CheatcodeOutcome::Continue(vec![state.block.number.clone()]))
1617            }
1618            txGasPriceCall::SELECTOR => {
1619                state.gas_price = state.memory.load_word(&mut self.cx, in_offset + 4)?;
1620                Ok(CheatcodeOutcome::Continue(Vec::new()))
1621            }
1622            getBlockTimestampCall::SELECTOR => {
1623                Ok(CheatcodeOutcome::Continue(vec![state.block.timestamp.clone()]))
1624            }
1625            labelCall::SELECTOR => {
1626                let values =
1627                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1628                let account = dyn_address(&values[0])?;
1629                let label = dyn_string(&values[1])?;
1630                state.labels.insert(account, label);
1631                Ok(CheatcodeOutcome::Continue(Vec::new()))
1632            }
1633            getLabelCall::SELECTOR => {
1634                let account = read_abi_address_arg(
1635                    &mut self.cx,
1636                    &state.memory,
1637                    args_offset,
1638                    0,
1639                    "symbolic vm.getLabel",
1640                )?;
1641                let label = state
1642                    .labels
1643                    .get(&account)
1644                    .cloned()
1645                    .unwrap_or_else(|| format!("unlabeled:{account}"));
1646                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1647                    &mut self.cx,
1648                    label.as_bytes(),
1649                )))
1650            }
1651            expectSafeMemoryCall::SELECTOR => {
1652                Err(SymbolicError::Unsupported("symbolic vm.expectSafeMemory not modeled"))
1653            }
1654            expectSafeMemoryCallCall::SELECTOR => {
1655                Err(SymbolicError::Unsupported("symbolic vm.expectSafeMemoryCall not modeled"))
1656            }
1657            stopExpectSafeMemoryCall::SELECTOR => {
1658                Err(SymbolicError::Unsupported("symbolic vm.stopExpectSafeMemory not modeled"))
1659            }
1660            lastCallGasCall::SELECTOR => {
1661                Err(SymbolicError::Unsupported("symbolic vm.lastCallGas not modeled"))
1662            }
1663            lastFrameGasCall::SELECTOR => {
1664                Err(SymbolicError::Unsupported("symbolic vm.lastFrameGas not modeled"))
1665            }
1666            snapshotGasLastCall_0Call::SELECTOR | snapshotGasLastCall_1Call::SELECTOR => {
1667                Err(SymbolicError::Unsupported("symbolic vm.snapshotGasLastCall not modeled"))
1668            }
1669            snapshotGasLastFrame_0Call::SELECTOR | snapshotGasLastFrame_1Call::SELECTOR => {
1670                Err(SymbolicError::Unsupported("symbolic vm.snapshotGasLastFrame not modeled"))
1671            }
1672            stopSnapshotGas_0Call::SELECTOR
1673            | stopSnapshotGas_1Call::SELECTOR
1674            | stopSnapshotGas_2Call::SELECTOR => {
1675                Err(SymbolicError::Unsupported("symbolic vm.stopSnapshotGas not modeled"))
1676            }
1677            pauseGasMeteringCall::SELECTOR
1678            | resumeGasMeteringCall::SELECTOR
1679            | resetGasMeteringCall::SELECTOR
1680            | breakpoint_0Call::SELECTOR
1681            | breakpoint_1Call::SELECTOR
1682            | snapshotValue_0Call::SELECTOR
1683            | snapshotValue_1Call::SELECTOR
1684            | startSnapshotGas_0Call::SELECTOR
1685            | startSnapshotGas_1Call::SELECTOR
1686            | sleepCall::SELECTOR
1687            | coolCall::SELECTOR
1688            | accessListCall::SELECTOR
1689            | warmSlotCall::SELECTOR
1690            | coolSlotCall::SELECTOR
1691            | noAccessListCall::SELECTOR => Ok(CheatcodeOutcome::Continue(Vec::new())),
1692            setEvmVersionCall::SELECTOR => {
1693                Err(SymbolicError::Unsupported("symbolic vm.setEvmVersion not modeled"))
1694            }
1695            getEvmVersionCall::SELECTOR => {
1696                Err(SymbolicError::Unsupported("symbolic vm.getEvmVersion not modeled"))
1697            }
1698            getFoundryVersionCall::SELECTOR => Ok(CheatcodeOutcome::ContinueData(
1699                abi_concrete_bytes_return(&mut self.cx, env!("CARGO_PKG_VERSION").as_bytes()),
1700            )),
1701            projectRootCall::SELECTOR => {
1702                self.stateless_retry_safe = false;
1703                let root = std::env::current_dir()
1704                    .map_err(|_| SymbolicError::Unsupported("symbolic vm.projectRoot"))?;
1705                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1706                    &mut self.cx,
1707                    root.display().to_string().as_bytes(),
1708                )))
1709            }
1710            unixTimeCall::SELECTOR => {
1711                self.stateless_retry_safe = false;
1712                let milliseconds = SystemTime::now()
1713                    .duration_since(UNIX_EPOCH)
1714                    .map_err(|_| SymbolicError::Unsupported("symbolic vm.unixTime"))?
1715                    .as_millis();
1716                let value = U256::try_from(milliseconds)
1717                    .map_err(|_| SymbolicError::Unsupported("symbolic vm.unixTime"))?;
1718                let value = SymExpr::constant(&mut self.cx, value);
1719                Ok(CheatcodeOutcome::Continue(vec![value]))
1720            }
1721            isIsolateModeCall::SELECTOR => {
1722                let isolate = SymExpr::constant(&mut self.cx, U256::from(executor.isolate));
1723                Ok(CheatcodeOutcome::Continue(vec![isolate]))
1724            }
1725            isContextCall::SELECTOR => {
1726                let context = read_abi_concrete_word_arg(
1727                    &mut self.cx,
1728                    &state.memory,
1729                    args_offset,
1730                    0,
1731                    "symbolic vm.isContext",
1732                )?;
1733                let context = u8::try_from(context)
1734                    .ok()
1735                    .and_then(|context| ForgeContext::try_from(context).ok())
1736                    .ok_or(SymbolicError::Unsupported("symbolic vm.isContext invalid context"))?;
1737                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(
1738                    &mut self.cx,
1739                    U256::from(current_execution_context() == Some(context)),
1740                )]))
1741            }
1742            toString_0Call::SELECTOR
1743            | toString_1Call::SELECTOR
1744            | toString_2Call::SELECTOR
1745            | toString_3Call::SELECTOR
1746            | toString_4Call::SELECTOR
1747            | toString_5Call::SELECTOR => {
1748                let output = match selector {
1749                    toString_0Call::SELECTOR => format!(
1750                        "{:?}",
1751                        read_abi_address_arg(
1752                            &mut self.cx,
1753                            &state.memory,
1754                            args_offset,
1755                            0,
1756                            "symbolic vm.toString"
1757                        )?
1758                    ),
1759                    toString_1Call::SELECTOR => hex::encode_prefixed(read_abi_dynamic_bytes_arg(
1760                        &mut self.cx,
1761                        &state.memory,
1762                        args_offset,
1763                        0,
1764                        "symbolic vm.toString",
1765                    )?),
1766                    toString_3Call::SELECTOR => read_abi_bool_arg(
1767                        &mut self.cx,
1768                        &state.memory,
1769                        args_offset,
1770                        0,
1771                        "symbolic vm.toString",
1772                    )?
1773                    .to_string(),
1774                    _ => {
1775                        let value = read_abi_concrete_word_arg(
1776                            &mut self.cx,
1777                            &state.memory,
1778                            args_offset,
1779                            0,
1780                            "symbolic vm.toString",
1781                        )?;
1782                        match selector {
1783                            toString_2Call::SELECTOR => {
1784                                hex::encode_prefixed(value.to_be_bytes::<32>())
1785                            }
1786                            toString_4Call::SELECTOR => value.to_string(),
1787                            _ => I256::from_raw(value).to_string(),
1788                        }
1789                    }
1790                };
1791                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1792                    &mut self.cx,
1793                    output.as_bytes(),
1794                )))
1795            }
1796            envBool_0Call::SELECTOR
1797            | envUint_0Call::SELECTOR
1798            | envInt_0Call::SELECTOR
1799            | envAddress_0Call::SELECTOR
1800            | envBytes32_0Call::SELECTOR
1801            | envString_0Call::SELECTOR
1802            | envBytes_0Call::SELECTOR
1803            | parseBoolCall::SELECTOR
1804            | parseUintCall::SELECTOR
1805            | parseIntCall::SELECTOR
1806            | parseAddressCall::SELECTOR
1807            | parseBytes32Call::SELECTOR
1808            | parseBytesCall::SELECTOR => {
1809                let (ty, message, from_env) = match selector {
1810                    envBool_0Call::SELECTOR => (DynSolType::Bool, "symbolic vm.envBool", true),
1811                    envUint_0Call::SELECTOR => (DynSolType::Uint(256), "symbolic vm.envUint", true),
1812                    envInt_0Call::SELECTOR => (DynSolType::Int(256), "symbolic vm.envInt", true),
1813                    envAddress_0Call::SELECTOR => {
1814                        (DynSolType::Address, "symbolic vm.envAddress", true)
1815                    }
1816                    envBytes32_0Call::SELECTOR => {
1817                        (DynSolType::FixedBytes(32), "symbolic vm.envBytes32", true)
1818                    }
1819                    envString_0Call::SELECTOR => {
1820                        (DynSolType::String, "symbolic vm.envString", true)
1821                    }
1822                    envBytes_0Call::SELECTOR => (DynSolType::Bytes, "symbolic vm.envBytes", true),
1823                    parseBoolCall::SELECTOR => (DynSolType::Bool, "symbolic vm.parseBool", false),
1824                    parseUintCall::SELECTOR => {
1825                        (DynSolType::Uint(256), "symbolic vm.parseUint", false)
1826                    }
1827                    parseIntCall::SELECTOR => (DynSolType::Int(256), "symbolic vm.parseInt", false),
1828                    parseAddressCall::SELECTOR => {
1829                        (DynSolType::Address, "symbolic vm.parseAddress", false)
1830                    }
1831                    parseBytes32Call::SELECTOR => {
1832                        (DynSolType::FixedBytes(32), "symbolic vm.parseBytes32", false)
1833                    }
1834                    _ => (DynSolType::Bytes, "symbolic vm.parseBytes", false),
1835                };
1836                let mut value =
1837                    read_abi_string_arg(&mut self.cx, &state.memory, args_offset, 0, message)?;
1838                if from_env {
1839                    self.stateless_retry_safe = false;
1840                    value = std::env::var(value)
1841                        .map_err(|_| SymbolicError::Unsupported("symbolic env var missing"))?;
1842                }
1843                let value = parse_env_value(&value, &ty)?;
1844                Ok(match value.as_word() {
1845                    Some(word) => CheatcodeOutcome::Continue(vec![SymExpr::constant(
1846                        &mut self.cx,
1847                        word.into(),
1848                    )]),
1849                    None => CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1850                        &mut self.cx,
1851                        &value.abi_encode_packed(),
1852                    )),
1853                })
1854            }
1855            toLowercaseCall::SELECTOR | toUppercaseCall::SELECTOR | trimCall::SELECTOR => {
1856                let value = read_abi_string_arg(
1857                    &mut self.cx,
1858                    &state.memory,
1859                    args_offset,
1860                    0,
1861                    "symbolic vm.string",
1862                )?;
1863                let output = if selector == toLowercaseCall::SELECTOR {
1864                    value.to_lowercase()
1865                } else if selector == toUppercaseCall::SELECTOR {
1866                    value.to_uppercase()
1867                } else {
1868                    value.trim().to_string()
1869                };
1870                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1871                    &mut self.cx,
1872                    output.as_bytes(),
1873                )))
1874            }
1875            replaceCall::SELECTOR => {
1876                let values =
1877                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1878                let output = dyn_string(&values[0])?
1879                    .replace(&dyn_string(&values[1])?, &dyn_string(&values[2])?);
1880                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1881                    &mut self.cx,
1882                    output.as_bytes(),
1883                )))
1884            }
1885            splitCall::SELECTOR => {
1886                let values =
1887                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1888                let input = dyn_string(&values[0])?;
1889                let delimiter = dyn_string(&values[1])?;
1890                let parts = if delimiter.is_empty() {
1891                    input.chars().map(|ch| DynSolValue::String(ch.to_string())).collect()
1892                } else {
1893                    input
1894                        .split(&delimiter)
1895                        .map(|part| DynSolValue::String(part.to_string()))
1896                        .collect()
1897                };
1898                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(
1899                    &mut self.cx,
1900                    DynSolValue::Array(parts),
1901                )))
1902            }
1903            indexOfCall::SELECTOR => {
1904                let values =
1905                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1906                let input = dyn_string(&values[0])?;
1907                let needle = dyn_string(&values[1])?;
1908                let index = input.find(&needle).map(U256::from).unwrap_or(U256::MAX);
1909                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(&mut self.cx, index)]))
1910            }
1911            containsCall::SELECTOR => {
1912                let values =
1913                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1914                let contains = dyn_string(&values[0])?.contains(&dyn_string(&values[1])?);
1915                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(
1916                    &mut self.cx,
1917                    U256::from(contains),
1918                )]))
1919            }
1920            toBase64_0Call::SELECTOR
1921            | toBase64_1Call::SELECTOR
1922            | toBase64URL_0Call::SELECTOR
1923            | toBase64URL_1Call::SELECTOR => {
1924                let data = read_abi_dynamic_bytes_arg(
1925                    &mut self.cx,
1926                    &state.memory,
1927                    args_offset,
1928                    0,
1929                    "symbolic vm.toBase64",
1930                )?;
1931                let encoded = if selector == toBase64URL_0Call::SELECTOR
1932                    || selector == toBase64URL_1Call::SELECTOR
1933                {
1934                    BASE64_URL_SAFE.encode(data)
1935                } else {
1936                    BASE64_STANDARD.encode(data)
1937                };
1938                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
1939                    &mut self.cx,
1940                    encoded.as_bytes(),
1941                )))
1942            }
1943            bound_0Call::SELECTOR | bound_1Call::SELECTOR => {
1944                self.handle_bound(state, args_offset, selector == bound_1Call::SELECTOR)
1945            }
1946            envExistsCall::SELECTOR => {
1947                let name = read_abi_string_arg(
1948                    &mut self.cx,
1949                    &state.memory,
1950                    args_offset,
1951                    0,
1952                    "symbolic vm.envExists",
1953                )?;
1954                self.stateless_retry_safe = false;
1955                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(
1956                    &mut self.cx,
1957                    U256::from(std::env::var_os(name).is_some()),
1958                )]))
1959            }
1960            envBool_1Call::SELECTOR
1961            | envUint_1Call::SELECTOR
1962            | envInt_1Call::SELECTOR
1963            | envAddress_1Call::SELECTOR
1964            | envBytes32_1Call::SELECTOR
1965            | envString_1Call::SELECTOR
1966            | envBytes_1Call::SELECTOR => {
1967                let name = VmCalls::name_by_selector(selector).unwrap_or_default();
1968                let element_ty = DynSolType::parse(&name["env".len()..].to_ascii_lowercase())
1969                    .map_err(|_| SymbolicError::Unsupported("symbolic env type"))?;
1970                let values =
1971                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
1972                let name = dyn_string(&values[0])?;
1973                let delimiter = dyn_string(&values[1])?;
1974                self.stateless_retry_safe = false;
1975                let value = std::env::var(name)
1976                    .map_err(|_| SymbolicError::Unsupported("symbolic env var missing"))?;
1977                let value = parse_env_array(&value, &delimiter, &element_ty)?;
1978                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(&mut self.cx, value)))
1979            }
1980            envOr_0Call::SELECTOR => {
1981                let name = read_abi_string_arg(
1982                    &mut self.cx,
1983                    &state.memory,
1984                    args_offset,
1985                    0,
1986                    "symbolic vm.envOr",
1987                )?;
1988                self.stateless_retry_safe = false;
1989                let value = match std::env::var(name) {
1990                    Ok(value) => U256::from(parse_env_bool(&value)?),
1991                    Err(_) => read_abi_concrete_word_arg(
1992                        &mut self.cx,
1993                        &state.memory,
1994                        args_offset,
1995                        1,
1996                        "symbolic vm.envOr",
1997                    )?,
1998                };
1999                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(&mut self.cx, value)]))
2000            }
2001            envOr_1Call::SELECTOR
2002            | envOr_2Call::SELECTOR
2003            | envOr_3Call::SELECTOR
2004            | envOr_4Call::SELECTOR => {
2005                let name = read_abi_string_arg(
2006                    &mut self.cx,
2007                    &state.memory,
2008                    args_offset,
2009                    0,
2010                    "symbolic vm.envOr",
2011                )?;
2012                let default = read_abi_concrete_word_arg(
2013                    &mut self.cx,
2014                    &state.memory,
2015                    args_offset,
2016                    1,
2017                    "symbolic vm.envOr",
2018                )?;
2019                self.stateless_retry_safe = false;
2020                let value = match std::env::var(name) {
2021                    Ok(value) if selector == envOr_1Call::SELECTOR => parse_env_uint(&value)?,
2022                    Ok(value) if selector == envOr_2Call::SELECTOR => parse_env_int(&value)?,
2023                    Ok(value) if selector == envOr_3Call::SELECTOR => {
2024                        address_word(parse_env_address(&value)?)
2025                    }
2026                    Ok(value) => parse_env_bytes32(&value)?,
2027                    Err(_) => default,
2028                };
2029                Ok(CheatcodeOutcome::Continue(vec![SymExpr::constant(&mut self.cx, value)]))
2030            }
2031            envOr_5Call::SELECTOR => {
2032                let values =
2033                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
2034                let name = dyn_string(&values[0])?;
2035                self.stateless_retry_safe = false;
2036                let value = std::env::var(name).unwrap_or(dyn_string(&values[1])?);
2037                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(
2038                    &mut self.cx,
2039                    value.as_bytes(),
2040                )))
2041            }
2042            envOr_6Call::SELECTOR => {
2043                let values =
2044                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
2045                let name = dyn_string(&values[0])?;
2046                self.stateless_retry_safe = false;
2047                let value = match std::env::var(name) {
2048                    Ok(value) => parse_env_bytes(&value)?,
2049                    Err(_) => dyn_bytes(&values[1])?,
2050                };
2051                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(&mut self.cx, &value)))
2052            }
2053            envOr_7Call::SELECTOR
2054            | envOr_8Call::SELECTOR
2055            | envOr_9Call::SELECTOR
2056            | envOr_10Call::SELECTOR
2057            | envOr_11Call::SELECTOR
2058            | envOr_12Call::SELECTOR
2059            | envOr_13Call::SELECTOR => {
2060                let params = vm_params(selector);
2061                let Some(DynSolType::Array(element_ty)) = params.last().cloned() else {
2062                    return Err(SymbolicError::Unsupported("symbolic env type"));
2063                };
2064                let values =
2065                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
2066                let name = dyn_string(&values[0])?;
2067                let delimiter = dyn_string(&values[1])?;
2068                self.stateless_retry_safe = false;
2069                let value = match std::env::var(name) {
2070                    Ok(value) => parse_env_array(&value, &delimiter, &element_ty)?,
2071                    Err(_) => values[2].clone(),
2072                };
2073                Ok(CheatcodeOutcome::ContinueData(abi_concrete_value_return(&mut self.cx, value)))
2074            }
2075            ffiCall::SELECTOR => {
2076                if !state.ffi_enabled {
2077                    return Err(SymbolicError::Unsupported("symbolic ffi disabled"));
2078                }
2079                let values =
2080                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
2081                let args = dyn_string_array(&values[0])?;
2082                if args.is_empty() || args[0].is_empty() {
2083                    return Err(SymbolicError::Unsupported("symbolic ffi empty command"));
2084                }
2085                self.stateless_retry_safe = false;
2086                let output = Command::new(&args[0])
2087                    .args(&args[1..])
2088                    .output()
2089                    .map_err(|_| SymbolicError::Unsupported("symbolic ffi command"))?;
2090                if !output.status.success() {
2091                    return Err(SymbolicError::Unsupported("symbolic ffi command failed"));
2092                }
2093                let stdout = String::from_utf8(output.stdout)
2094                    .map_err(|_| SymbolicError::Unsupported("symbolic ffi stdout"))?;
2095                let trimmed = stdout.trim();
2096                let bytes = hex::decode(trimmed).unwrap_or_else(|_| trimmed.as_bytes().to_vec());
2097                Ok(CheatcodeOutcome::ContinueData(abi_concrete_bytes_return(&mut self.cx, &bytes)))
2098            }
2099            assertTrue_0Call::SELECTOR | assertTrue_1Call::SELECTOR => {
2100                let word = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2101                let condition = word.nonzero_bool(&mut self.cx);
2102                self.handle_assertion(state, condition)
2103            }
2104            assertFalse_0Call::SELECTOR | assertFalse_1Call::SELECTOR => {
2105                let word = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2106                let condition = word.into_zero_bool(&mut self.cx);
2107                self.handle_assertion(state, condition)
2108            }
2109            assertEq_2Call::SELECTOR
2110            | assertEq_3Call::SELECTOR
2111            | assertEq_4Call::SELECTOR
2112            | assertEq_5Call::SELECTOR
2113            | assertEq_6Call::SELECTOR
2114            | assertEq_7Call::SELECTOR
2115            | assertEq_8Call::SELECTOR
2116            | assertEq_9Call::SELECTOR
2117            | assertEq_0Call::SELECTOR
2118            | assertEq_1Call::SELECTOR => {
2119                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2120                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2121                let condition = SymBoolExpr::eq(&mut self.cx, left, right);
2122                self.handle_assertion(state, condition)
2123            }
2124            assertEqDecimal_0Call::SELECTOR
2125            | assertEqDecimal_1Call::SELECTOR
2126            | assertEqDecimal_2Call::SELECTOR
2127            | assertEqDecimal_3Call::SELECTOR => {
2128                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2129                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2130                let condition = SymBoolExpr::eq(&mut self.cx, left, right);
2131                self.handle_assertion(state, condition)
2132            }
2133            assertNotEq_2Call::SELECTOR
2134            | assertNotEq_3Call::SELECTOR
2135            | assertNotEq_4Call::SELECTOR
2136            | assertNotEq_5Call::SELECTOR
2137            | assertNotEq_6Call::SELECTOR
2138            | assertNotEq_7Call::SELECTOR
2139            | assertNotEq_8Call::SELECTOR
2140            | assertNotEq_9Call::SELECTOR
2141            | assertNotEq_0Call::SELECTOR
2142            | assertNotEq_1Call::SELECTOR => {
2143                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2144                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2145                let condition = SymBoolExpr::eq(&mut self.cx, left, right);
2146                let condition = condition.not(&mut self.cx);
2147                self.handle_assertion(state, condition)
2148            }
2149            assertEq_10Call::SELECTOR
2150            | assertEq_11Call::SELECTOR
2151            | assertEq_12Call::SELECTOR
2152            | assertEq_13Call::SELECTOR
2153            | assertEq_14Call::SELECTOR
2154            | assertEq_15Call::SELECTOR
2155            | assertEq_16Call::SELECTOR
2156            | assertEq_17Call::SELECTOR
2157            | assertEq_18Call::SELECTOR
2158            | assertEq_19Call::SELECTOR
2159            | assertEq_20Call::SELECTOR
2160            | assertEq_21Call::SELECTOR
2161            | assertEq_22Call::SELECTOR
2162            | assertEq_23Call::SELECTOR
2163            | assertEq_24Call::SELECTOR
2164            | assertEq_25Call::SELECTOR
2165            | assertEq_26Call::SELECTOR
2166            | assertEq_27Call::SELECTOR
2167            | assertNotEq_10Call::SELECTOR
2168            | assertNotEq_11Call::SELECTOR
2169            | assertNotEq_12Call::SELECTOR
2170            | assertNotEq_13Call::SELECTOR
2171            | assertNotEq_14Call::SELECTOR
2172            | assertNotEq_15Call::SELECTOR
2173            | assertNotEq_16Call::SELECTOR
2174            | assertNotEq_17Call::SELECTOR
2175            | assertNotEq_18Call::SELECTOR
2176            | assertNotEq_19Call::SELECTOR
2177            | assertNotEq_20Call::SELECTOR
2178            | assertNotEq_21Call::SELECTOR
2179            | assertNotEq_22Call::SELECTOR
2180            | assertNotEq_23Call::SELECTOR
2181            | assertNotEq_24Call::SELECTOR
2182            | assertNotEq_25Call::SELECTOR
2183            | assertNotEq_26Call::SELECTOR
2184            | assertNotEq_27Call::SELECTOR => {
2185                let values =
2186                    decode_cheatcode_args(&mut self.cx, state, selector, in_offset, in_size)?;
2187                let expect_equal = VmCalls::name_by_selector(selector) == Some("assertEq");
2188                let condition =
2189                    SymBoolExpr::constant(&mut self.cx, (values[0] == values[1]) == expect_equal);
2190                self.handle_assertion(state, condition)
2191            }
2192            assertLt_0Call::SELECTOR | assertLt_1Call::SELECTOR => {
2193                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2194                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2195                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Ult, left, right);
2196                self.handle_assertion(state, condition)
2197            }
2198            assertLe_0Call::SELECTOR | assertLe_1Call::SELECTOR => {
2199                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2200                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2201                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Ule, left, right);
2202                self.handle_assertion(state, condition)
2203            }
2204            assertGt_0Call::SELECTOR | assertGt_1Call::SELECTOR => {
2205                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2206                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2207                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Ugt, left, right);
2208                self.handle_assertion(state, condition)
2209            }
2210            assertGe_0Call::SELECTOR | assertGe_1Call::SELECTOR => {
2211                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2212                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2213                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Uge, left, right);
2214                self.handle_assertion(state, condition)
2215            }
2216            assertLt_2Call::SELECTOR | assertLt_3Call::SELECTOR => {
2217                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2218                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2219                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Slt, left, right);
2220                self.handle_assertion(state, condition)
2221            }
2222            assertGt_2Call::SELECTOR | assertGt_3Call::SELECTOR => {
2223                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2224                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2225                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Sgt, left, right);
2226                self.handle_assertion(state, condition)
2227            }
2228            assertLe_2Call::SELECTOR | assertLe_3Call::SELECTOR => {
2229                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2230                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2231                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Sgt, left, right);
2232                let condition = condition.not(&mut self.cx);
2233                self.handle_assertion(state, condition)
2234            }
2235            assertGe_2Call::SELECTOR | assertGe_3Call::SELECTOR => {
2236                let left = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2237                let right = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 1)?;
2238                let condition = SymBoolExpr::cmp(&mut self.cx, SymCmpOp::Slt, left, right);
2239                let condition = condition.not(&mut self.cx);
2240                self.handle_assertion(state, condition)
2241            }
2242            randomUint_0Call::SELECTOR => {
2243                Ok(CheatcodeOutcome::Continue(vec![state.fresh_word(&mut self.cx, "vmRandomUint")]))
2244            }
2245            randomUint_2Call::SELECTOR => {
2246                let bits = read_abi_constrained_word_arg(
2247                    &mut self.cx,
2248                    state,
2249                    args_offset,
2250                    0,
2251                    "symbolic randomUint bits",
2252                )?;
2253                Self::validate_symbolic_integer_bits(bits, "symbolic randomUint bits")?;
2254                Ok(CheatcodeOutcome::Continue(vec![state.fresh_bounded_uint(&mut self.cx, bits)]))
2255            }
2256            randomUint_1Call::SELECTOR => {
2257                let min = state.memory.load_word(&mut self.cx, in_offset + 4)?;
2258                let max = state.memory.load_word(&mut self.cx, in_offset + 36)?;
2259                let value = state.fresh_word(&mut self.cx, "vmRandomUintRange");
2260                state.constraints.push(SymBoolExpr::cmp_word_expr(
2261                    &mut self.cx,
2262                    SymCmpOp::Uge,
2263                    &value,
2264                    min,
2265                ));
2266                state.constraints.push(SymBoolExpr::cmp_word_expr(
2267                    &mut self.cx,
2268                    SymCmpOp::Ule,
2269                    &value,
2270                    max,
2271                ));
2272                Ok(CheatcodeOutcome::Continue(vec![value]))
2273            }
2274            randomInt_0Call::SELECTOR => {
2275                Ok(CheatcodeOutcome::Continue(vec![state.fresh_word(&mut self.cx, "vmRandomInt")]))
2276            }
2277            randomInt_1Call::SELECTOR => {
2278                let bits = read_abi_constrained_word_arg(
2279                    &mut self.cx,
2280                    state,
2281                    args_offset,
2282                    0,
2283                    "symbolic randomInt bits",
2284                )?;
2285                Self::validate_symbolic_integer_bits(bits, "symbolic randomInt bits")?;
2286                Ok(CheatcodeOutcome::Continue(vec![state.fresh_bounded_int(&mut self.cx, bits)]))
2287            }
2288            randomAddressCall::SELECTOR => {
2289                let value = state.fresh_bounded_uint(&mut self.cx, U256::from(160));
2290                Ok(CheatcodeOutcome::Continue(vec![value]))
2291            }
2292            randomBoolCall::SELECTOR => {
2293                let value = state.fresh_bounded_uint(&mut self.cx, U256::ONE);
2294                Ok(CheatcodeOutcome::Continue(vec![value]))
2295            }
2296            randomBytesCall::SELECTOR => {
2297                let len = read_abi_word_arg(&mut self.cx, &state.memory, args_offset, 0)?;
2298                let max_limit = self.config.max_dynamic_length as usize;
2299                let max_len = self.solver_upper_bound_usize(
2300                    state,
2301                    &len,
2302                    max_limit,
2303                    "symbolic randomBytes length",
2304                )?;
2305                let bytes = state.fresh_bytes(&mut self.cx, max_len);
2306                Ok(CheatcodeOutcome::ContinueData(abi_bytes_return_with_len(
2307                    &mut self.cx,
2308                    len,
2309                    bytes,
2310                )))
2311            }
2312            randomBytes4Call::SELECTOR => {
2313                let value = state.fresh_bounded_uint(&mut self.cx, U256::from(32));
2314                Ok(CheatcodeOutcome::Continue(vec![shift_left(&mut self.cx, value, 224)]))
2315            }
2316            randomBytes8Call::SELECTOR => {
2317                let value = state.fresh_bounded_uint(&mut self.cx, U256::from(64));
2318                Ok(CheatcodeOutcome::Continue(vec![shift_left(&mut self.cx, value, 192)]))
2319            }
2320
2321            _ => Err(SymbolicError::Unsupported("symbolic Foundry cheatcode")),
2322        }
2323    }
2324
2325    pub(super) fn handle_symbolic_vm_cheatcode(
2326        &mut self,
2327        state: &mut PathState,
2328        selector: [u8; 4],
2329        in_offset: usize,
2330    ) -> Result<SymReturnData, SymbolicError> {
2331        let Some(cheatcode) = SymbolicVmCheatcode::from_selector(selector) else {
2332            return Err(SymbolicError::Unsupported("symbolic VM compatibility cheatcode"));
2333        };
2334        let args_offset = in_offset + 4;
2335
2336        match cheatcode {
2337            SymbolicVmCheatcode::CreateUintBits(bits) => {
2338                let value = if bits == 256 {
2339                    state.fresh_word(&mut self.cx, "svm")
2340                } else {
2341                    state.fresh_bounded_uint(&mut self.cx, U256::from(bits))
2342                };
2343                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2344            }
2345            SymbolicVmCheatcode::CreateIntBits(bits) => {
2346                let value = if bits == 256 {
2347                    state.fresh_word(&mut self.cx, "svm")
2348                } else {
2349                    state.fresh_bounded_int(&mut self.cx, U256::from(bits))
2350                };
2351                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2352            }
2353            SymbolicVmCheatcode::CreateBytesFixed(bytes) => {
2354                let value = if bytes == 32 {
2355                    state.fresh_word(&mut self.cx, "svm")
2356                } else {
2357                    let value = state.fresh_bounded_uint(&mut self.cx, U256::from(bytes * 8));
2358                    shift_left(&mut self.cx, value, (32 - bytes) * 8)
2359                };
2360                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2361            }
2362            SymbolicVmCheatcode::CreateUint => {
2363                let bits = read_abi_constrained_word_arg(
2364                    &mut self.cx,
2365                    state,
2366                    args_offset,
2367                    0,
2368                    "symbolic svm.create integer bits",
2369                )?;
2370                Self::validate_symbolic_integer_bits(bits, "symbolic svm.create integer bits")?;
2371                let value = state.fresh_bounded_uint(&mut self.cx, bits);
2372                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2373            }
2374            SymbolicVmCheatcode::CreateInt => {
2375                let bits = read_abi_constrained_word_arg(
2376                    &mut self.cx,
2377                    state,
2378                    args_offset,
2379                    0,
2380                    "symbolic svm.create integer bits",
2381                )?;
2382                Self::validate_symbolic_integer_bits(bits, "symbolic svm.create integer bits")?;
2383                let value = state.fresh_bounded_int(&mut self.cx, bits);
2384                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2385            }
2386            SymbolicVmCheatcode::CreateAddress => {
2387                let value = state.fresh_bounded_uint(&mut self.cx, U256::from(160));
2388                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2389            }
2390            SymbolicVmCheatcode::CreateBool => {
2391                let value = state.fresh_bounded_uint(&mut self.cx, U256::ONE);
2392                Ok(SymReturnData::from_words(&mut self.cx, vec![value]))
2393            }
2394            SymbolicVmCheatcode::CreateBytes => {
2395                let bytes =
2396                    state.fresh_bytes(&mut self.cx, self.config.default_dynamic_length as usize);
2397                Ok(abi_bytes_return(&mut self.cx, bytes))
2398            }
2399            SymbolicVmCheatcode::CreateBytesSized => {
2400                let len = read_abi_constrained_word_arg(
2401                    &mut self.cx,
2402                    state,
2403                    args_offset,
2404                    0,
2405                    "symbolic svm.createBytes length",
2406                )?;
2407                let len = usize::try_from(len)
2408                    .ok()
2409                    .filter(|len| *len <= self.config.max_calldata_bytes as usize)
2410                    .ok_or(SymbolicError::Unsupported("symbolic svm.createBytes length"))?;
2411                let bytes = state.fresh_bytes(&mut self.cx, len);
2412                Ok(abi_bytes_return(&mut self.cx, bytes))
2413            }
2414            SymbolicVmCheatcode::CreateString => {
2415                let bytes = state.fresh_printable_ascii_bytes(
2416                    &mut self.cx,
2417                    self.config.default_dynamic_length as usize,
2418                );
2419                Ok(abi_bytes_return(&mut self.cx, bytes))
2420            }
2421            SymbolicVmCheatcode::CreateStringSized => {
2422                let len = read_abi_constrained_word_arg(
2423                    &mut self.cx,
2424                    state,
2425                    args_offset,
2426                    0,
2427                    "symbolic svm.createString length",
2428                )?;
2429                let len = usize::try_from(len)
2430                    .ok()
2431                    .filter(|len| *len <= self.config.max_calldata_bytes as usize)
2432                    .ok_or(SymbolicError::Unsupported("symbolic svm.createString length"))?;
2433                let bytes = state.fresh_printable_ascii_bytes(&mut self.cx, len);
2434                Ok(abi_bytes_return(&mut self.cx, bytes))
2435            }
2436            SymbolicVmCheatcode::CreateCalldata => {
2437                let max = self.config.max_calldata_bytes as usize;
2438                let len = if max < 4 {
2439                    max
2440                } else {
2441                    (self.config.default_dynamic_length as usize).max(4).min(max)
2442                };
2443                let bytes = state.fresh_bytes(&mut self.cx, len);
2444                Ok(abi_bytes_return(&mut self.cx, bytes))
2445            }
2446            SymbolicVmCheatcode::EnableSymbolicStorage => {
2447                let target =
2448                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
2449                state.world.enable_arbitrary_storage(target, false);
2450                Ok(SymReturnData::empty(&mut self.cx))
2451            }
2452            SymbolicVmCheatcode::SnapshotStorage => {
2453                let _target =
2454                    read_abi_address_or_symbolic_slot_arg(&mut self.cx, state, args_offset, 0)?;
2455                let id = state.world.snapshot_state();
2456                let id = SymExpr::constant(&mut self.cx, id);
2457                Ok(SymReturnData::from_words(&mut self.cx, vec![id]))
2458            }
2459            SymbolicVmCheatcode::SnapshotState => {
2460                let id = state.world.snapshot_state();
2461                let id = SymExpr::constant(&mut self.cx, id);
2462                Ok(SymReturnData::from_words(&mut self.cx, vec![id]))
2463            }
2464        }
2465    }
2466}