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 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}