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