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