1use foundry_cheatcodes_spec::Vm::*;
2use foundry_common::wallet::private_key_from_u256;
3
4use super::*;
5
6pub(crate) const fn foundry_cheatcode_min_input_size(selector: [u8; 4]) -> Option<usize> {
7 match selector {
8 recordLogsCall::SELECTOR
9 | recordCall::SELECTOR
10 | stopRecordCall::SELECTOR
11 | assumeNoRevert_0Call::SELECTOR
12 | getRecordedLogsCall::SELECTOR
13 | getRecordedLogsJsonCall::SELECTOR
14 | expectRevert_0Call::SELECTOR
15 | expectEmit_2Call::SELECTOR
16 | expectEmitAnonymous_2Call::SELECTOR
17 | clearMockedCallsCall::SELECTOR
18 | stopPrankCall::SELECTOR
19 | readCallersCall::SELECTOR
20 | getWalletsCall::SELECTOR
21 | snapshotCall::SELECTOR
22 | snapshotStateCall::SELECTOR
23 | deleteSnapshotsCall::SELECTOR
24 | deleteStateSnapshotsCall::SELECTOR
25 | activeForkCall::SELECTOR
26 | getChainIdCall::SELECTOR
27 | getBlobhashesCall::SELECTOR
28 | getBlobBaseFeeCall::SELECTOR
29 | getBlockNumberCall::SELECTOR
30 | getBlockTimestampCall::SELECTOR
31 | pauseGasMeteringCall::SELECTOR
32 | resumeGasMeteringCall::SELECTOR
33 | resetGasMeteringCall::SELECTOR
34 | isIsolateModeCall::SELECTOR
35 | lastCallGasCall::SELECTOR
36 | lastFrameGasCall::SELECTOR
37 | stopExpectSafeMemoryCall::SELECTOR
38 | stopSnapshotGas_0Call::SELECTOR
39 | getEvmVersionCall::SELECTOR
40 | getFoundryVersionCall::SELECTOR
41 | projectRootCall::SELECTOR
42 | unixTimeCall::SELECTOR
43 | noAccessListCall::SELECTOR
44 | randomUint_0Call::SELECTOR
45 | randomInt_0Call::SELECTOR
46 | randomAddressCall::SELECTOR
47 | randomBoolCall::SELECTOR
48 | randomBytes4Call::SELECTOR
49 | randomBytes8Call::SELECTOR => Some(abi_static_input_size(0)),
50 assumeCall::SELECTOR
51 | skip_0Call::SELECTOR
52 | skip_1Call::SELECTOR
53 | assumeNoRevert_1Call::SELECTOR
54 | assumeNoRevert_2Call::SELECTOR
55 | accessesCall::SELECTOR
56 | expectRevert_1Call::SELECTOR
57 | expectRevert_3Call::SELECTOR
58 | expectRevert_6Call::SELECTOR
59 | expectPartialRevert_0Call::SELECTOR
60 | expectEmit_3Call::SELECTOR
61 | expectEmit_6Call::SELECTOR
62 | expectEmitAnonymous_3Call::SELECTOR
63 | prank_0Call::SELECTOR
64 | startPrank_0Call::SELECTOR
65 | addrCall::SELECTOR
66 | rememberKeyCall::SELECTOR
67 | toString_0Call::SELECTOR
68 | toString_1Call::SELECTOR
69 | toString_2Call::SELECTOR
70 | toString_3Call::SELECTOR
71 | toString_4Call::SELECTOR
72 | toString_5Call::SELECTOR
73 | parseBytesCall::SELECTOR
74 | parseAddressCall::SELECTOR
75 | parseUintCall::SELECTOR
76 | parseIntCall::SELECTOR
77 | parseBytes32Call::SELECTOR
78 | parseBoolCall::SELECTOR
79 | toLowercaseCall::SELECTOR
80 | toUppercaseCall::SELECTOR
81 | trimCall::SELECTOR
82 | toBase64_0Call::SELECTOR
83 | toBase64_1Call::SELECTOR
84 | toBase64URL_0Call::SELECTOR
85 | toBase64URL_1Call::SELECTOR
86 | breakpoint_0Call::SELECTOR
87 | setEvmVersionCall::SELECTOR
88 | sleepCall::SELECTOR
89 | accessListCall::SELECTOR
90 | getNonce_0Call::SELECTOR
91 | resetNonceCall::SELECTOR
92 | allowCheatcodesCall::SELECTOR
93 | makePersistent_0Call::SELECTOR
94 | makePersistent_3Call::SELECTOR
95 | revokePersistent_0Call::SELECTOR
96 | revokePersistent_1Call::SELECTOR
97 | isPersistentCall::SELECTOR
98 | selectForkCall::SELECTOR
99 | createFork_0Call::SELECTOR
100 | createSelectFork_0Call::SELECTOR
101 | rollFork_0Call::SELECTOR
102 | rollFork_1Call::SELECTOR
103 | revertToCall::SELECTOR
104 | revertToStateCall::SELECTOR
105 | revertToAndDeleteCall::SELECTOR
106 | revertToStateAndDeleteCall::SELECTOR
107 | deleteSnapshotCall::SELECTOR
108 | deleteStateSnapshotCall::SELECTOR
109 | warpCall::SELECTOR
110 | rollCall::SELECTOR
111 | prevrandao_0Call::SELECTOR
112 | prevrandao_1Call::SELECTOR
113 | feeCall::SELECTOR
114 | blobBaseFeeCall::SELECTOR
115 | chainIdCall::SELECTOR
116 | difficultyCall::SELECTOR
117 | coinbaseCall::SELECTOR
118 | txGasPriceCall::SELECTOR
119 | getLabelCall::SELECTOR
120 | snapshotGasLastCall_0Call::SELECTOR
121 | snapshotGasLastFrame_0Call::SELECTOR
122 | startSnapshotGas_0Call::SELECTOR
123 | stopSnapshotGas_1Call::SELECTOR
124 | coolCall::SELECTOR
125 | isContextCall::SELECTOR
126 | assertTrue_0Call::SELECTOR
127 | assertTrue_1Call::SELECTOR
128 | assertFalse_0Call::SELECTOR
129 | assertFalse_1Call::SELECTOR
130 | randomUint_2Call::SELECTOR
131 | randomInt_1Call::SELECTOR
132 | randomBytesCall::SELECTOR => Some(abi_static_input_size(1)),
133 expectRevert_4Call::SELECTOR
134 | expectRevert_7Call::SELECTOR
135 | expectRevert_9Call::SELECTOR
136 | expectPartialRevert_1Call::SELECTOR
137 | expectEmit_7Call::SELECTOR
138 | prank_1Call::SELECTOR
139 | prank_2Call::SELECTOR
140 | startPrank_1Call::SELECTOR
141 | startPrank_2Call::SELECTOR
142 | sign_1Call::SELECTOR
143 | signCompact_1Call::SELECTOR
144 | deriveKey_0Call::SELECTOR
145 | splitCall::SELECTOR
146 | indexOfCall::SELECTOR
147 | containsCall::SELECTOR
148 | breakpoint_1Call::SELECTOR
149 | expectSafeMemoryCall::SELECTOR
150 | expectSafeMemoryCallCall::SELECTOR
151 | loadCall::SELECTOR
152 | makePersistent_1Call::SELECTOR
153 | computeCreateAddressCall::SELECTOR
154 | computeCreate2Address_1Call::SELECTOR
155 | setBlockhashCall::SELECTOR
156 | dealCall::SELECTOR
157 | setNonceCall::SELECTOR
158 | setNonceUnsafeCall::SELECTOR
159 | createFork_1Call::SELECTOR
160 | createFork_2Call::SELECTOR
161 | createSelectFork_1Call::SELECTOR
162 | createSelectFork_2Call::SELECTOR
163 | rollFork_2Call::SELECTOR
164 | rollFork_3Call::SELECTOR
165 | labelCall::SELECTOR
166 | snapshotValue_0Call::SELECTOR
167 | snapshotGasLastCall_1Call::SELECTOR
168 | snapshotGasLastFrame_1Call::SELECTOR
169 | startSnapshotGas_1Call::SELECTOR
170 | stopSnapshotGas_2Call::SELECTOR
171 | warmSlotCall::SELECTOR
172 | coolSlotCall::SELECTOR
173 | assertEq_2Call::SELECTOR
174 | assertEq_3Call::SELECTOR
175 | assertEq_4Call::SELECTOR
176 | assertEq_5Call::SELECTOR
177 | assertEq_6Call::SELECTOR
178 | assertEq_7Call::SELECTOR
179 | assertEq_8Call::SELECTOR
180 | assertEq_9Call::SELECTOR
181 | assertEq_0Call::SELECTOR
182 | assertEq_1Call::SELECTOR
183 | assertNotEq_2Call::SELECTOR
184 | assertNotEq_3Call::SELECTOR
185 | assertNotEq_4Call::SELECTOR
186 | assertNotEq_5Call::SELECTOR
187 | assertNotEq_6Call::SELECTOR
188 | assertNotEq_7Call::SELECTOR
189 | assertNotEq_8Call::SELECTOR
190 | assertNotEq_9Call::SELECTOR
191 | assertNotEq_0Call::SELECTOR
192 | assertNotEq_1Call::SELECTOR
193 | assertLt_0Call::SELECTOR
194 | assertLt_1Call::SELECTOR
195 | assertLe_0Call::SELECTOR
196 | assertLe_1Call::SELECTOR
197 | assertGt_0Call::SELECTOR
198 | assertGt_1Call::SELECTOR
199 | assertGe_0Call::SELECTOR
200 | assertGe_1Call::SELECTOR
201 | assertLt_2Call::SELECTOR
202 | assertLt_3Call::SELECTOR
203 | assertGt_2Call::SELECTOR
204 | assertGt_3Call::SELECTOR
205 | assertLe_2Call::SELECTOR
206 | assertLe_3Call::SELECTOR
207 | assertGe_2Call::SELECTOR
208 | assertGe_3Call::SELECTOR
209 | assertEq_14Call::SELECTOR
210 | assertEq_15Call::SELECTOR
211 | assertEq_16Call::SELECTOR
212 | assertEq_17Call::SELECTOR
213 | assertEq_18Call::SELECTOR
214 | assertEq_19Call::SELECTOR
215 | assertEq_20Call::SELECTOR
216 | assertEq_21Call::SELECTOR
217 | assertEq_22Call::SELECTOR
218 | assertEq_23Call::SELECTOR
219 | assertEq_24Call::SELECTOR
220 | assertEq_25Call::SELECTOR
221 | assertEq_26Call::SELECTOR
222 | assertEq_27Call::SELECTOR
223 | assertNotEq_14Call::SELECTOR
224 | assertNotEq_15Call::SELECTOR
225 | assertNotEq_16Call::SELECTOR
226 | assertNotEq_17Call::SELECTOR
227 | assertNotEq_18Call::SELECTOR
228 | assertNotEq_19Call::SELECTOR
229 | assertNotEq_20Call::SELECTOR
230 | assertNotEq_21Call::SELECTOR
231 | assertNotEq_22Call::SELECTOR
232 | assertNotEq_23Call::SELECTOR
233 | assertNotEq_24Call::SELECTOR
234 | assertNotEq_25Call::SELECTOR
235 | assertNotEq_26Call::SELECTOR
236 | assertNotEq_27Call::SELECTOR
237 | randomUint_1Call::SELECTOR => Some(abi_static_input_size(2)),
238 expectRevert_10Call::SELECTOR
239 | deriveKey_1Call::SELECTOR
240 | deriveKey_2Call::SELECTOR
241 | rememberKeys_0Call::SELECTOR
242 | storeCall::SELECTOR
243 | makePersistent_2Call::SELECTOR
244 | snapshotValue_1Call::SELECTOR
245 | replaceCall::SELECTOR
246 | assertEqDecimal_0Call::SELECTOR
247 | assertEqDecimal_2Call::SELECTOR
248 | computeCreate2Address_0Call::SELECTOR
249 | bound_0Call::SELECTOR
250 | bound_1Call::SELECTOR => Some(abi_static_input_size(3)),
251 expectEmit_0Call::SELECTOR
252 | deriveKey_3Call::SELECTOR
253 | rememberKeys_1Call::SELECTOR
254 | assertEqDecimal_1Call::SELECTOR
255 | assertEqDecimal_3Call::SELECTOR => Some(abi_static_input_size(4)),
256 expectEmitAnonymous_0Call::SELECTOR
257 | expectEmit_1Call::SELECTOR
258 | expectEmit_4Call::SELECTOR => Some(abi_static_input_size(5)),
259 expectEmit_5Call::SELECTOR | expectEmitAnonymous_1Call::SELECTOR => {
260 Some(abi_static_input_size(6))
261 }
262 _ => None,
263 }
264}
265
266pub(crate) fn symbolic_vm_cheatcode_min_input_size(selector: [u8; 4]) -> Option<usize> {
267 SymbolicVmCheatcode::from_selector(selector)
268 .map(|cheatcode| abi_static_input_size(cheatcode.min_input_words()))
269}
270
271pub(crate) const fn abi_static_input_size(words: usize) -> usize {
272 4 + words * 32
273}
274
275pub(crate) fn abi_bytes_return(cx: &mut SymCx, bytes: Vec<SymExpr>) -> SymReturnData {
276 let len = SymExpr::constant(cx, U256::from(bytes.len()));
277 abi_bytes_return_with_len(cx, len, bytes)
278}
279
280pub(crate) fn abi_bytes_return_with_len(
281 cx: &mut SymCx,
282 len: SymExpr,
283 bytes: Vec<SymExpr>,
284) -> SymReturnData {
285 let mut out = SymExpr::constant(cx, U256::from(32)).into_byte_exprs(cx);
286 out.extend(len.into_byte_exprs(cx));
287 out.extend(bytes.iter().cloned());
288 out.resize_with(64 + bytes.len().next_multiple_of(32), || SymExpr::zero(cx));
289 SymReturnData::from_byte_exprs(cx, out)
290}
291
292pub(crate) fn abi_concrete_bytes_return(cx: &mut SymCx, bytes: &[u8]) -> SymReturnData {
293 let bytes = bytes.iter().map(|byte| SymExpr::constant(cx, U256::from(*byte))).collect();
294 abi_bytes_return(cx, bytes)
295}
296
297pub(crate) fn abi_concrete_value_return(cx: &mut SymCx, value: DynSolValue) -> SymReturnData {
298 let bytes = value
299 .abi_encode()
300 .into_iter()
301 .map(|byte| SymExpr::constant(cx, U256::from(byte)))
302 .collect();
303 SymReturnData::from_byte_exprs(cx, bytes)
304}
305
306pub(crate) fn recorded_logs_return_data(cx: &mut SymCx, logs: Vec<SymbolicLog>) -> SymReturnData {
307 let value = SymbolicAbiValue::Array {
308 elements: logs
309 .into_iter()
310 .map(|log| {
311 let (topics, data_len, data, emitter) = log.into_parts();
312 let topics = topics
313 .iter()
314 .cloned()
315 .map(|topic| SymbolicAbiValue::FixedBytes {
316 bytes: topic.into_bytes(cx),
317 size: 32,
318 })
319 .collect();
320 SymbolicAbiValue::Tuple {
321 elements: vec![
322 SymbolicAbiValue::Array { elements: topics },
323 SymbolicAbiValue::Bytes { len: data_len, bytes: data },
324 SymbolicAbiValue::Address {
325 word: SymExpr::constant(cx, address_word(emitter)),
326 },
327 ],
328 }
329 })
330 .collect(),
331 };
332 let bytes = encode_sequence(cx, std::iter::once(&value));
333 SymReturnData::from_bytes(cx, bytes)
334}
335
336pub(crate) fn recorded_logs_json_return_data(
337 cx: &mut SymCx,
338 logs: Vec<SymbolicLog>,
339) -> Result<SymReturnData, SymbolicError> {
340 let mut bytes = Vec::new();
341 push_ascii(cx, &mut bytes, "[");
342 for (log_idx, log) in logs.into_iter().enumerate() {
343 let (topics, data_len, data, emitter) = log.into_parts();
344 if log_idx > 0 {
345 push_ascii(cx, &mut bytes, ",");
346 }
347 push_ascii(cx, &mut bytes, "{\"topics\":[");
348 for (topic_idx, topic) in topics.iter().cloned().enumerate() {
349 if topic_idx > 0 {
350 push_ascii(cx, &mut bytes, ",");
351 }
352 push_ascii(cx, &mut bytes, "\"0x");
353 push_hex_word(cx, &mut bytes, topic);
354 push_ascii(cx, &mut bytes, "\"");
355 }
356 push_ascii(cx, &mut bytes, "],\"data\":\"0x");
357
358 let len = data_len.as_const_or("symbolic vm.getRecordedLogsJson data length").and_then(
359 |len| {
360 usize::try_from(len).map_err(|_| {
361 SymbolicError::Unsupported("symbolic vm.getRecordedLogsJson data length")
362 })
363 },
364 )?;
365 if len > data.len() {
366 return Err(SymbolicError::Unsupported("symbolic vm.getRecordedLogsJson data length"));
367 }
368 for idx in 0..len {
369 let byte = data.byte(cx, idx);
370 push_hex_byte(cx, &mut bytes, byte);
371 }
372
373 push_ascii(cx, &mut bytes, "\",\"emitter\":\"");
374 push_ascii(cx, &mut bytes, &format!("{emitter}"));
375 push_ascii(cx, &mut bytes, "\"}");
376 }
377 push_ascii(cx, &mut bytes, "]");
378 Ok(abi_bytes_return(cx, bytes))
379}
380
381pub(crate) fn accesses_return_data(
382 cx: &mut SymCx,
383 record: Option<&AccessRecord>,
384 target: Address,
385) -> SymReturnData {
386 let reads = record.map(|record| record.read_slots(target)).unwrap_or_default();
387 let writes = record.map(|record| record.write_slots(target)).unwrap_or_default();
388 let values = [storage_slots_abi_array(cx, reads), storage_slots_abi_array(cx, writes)];
389 let bytes = encode_sequence(cx, values.iter());
390 SymReturnData::from_bytes(cx, bytes)
391}
392
393pub(crate) fn complete_cheatcode_call(
394 cx: &mut SymCx,
395 state: &mut PathState,
396 out_offset: SymExpr,
397 out_size: &BoundedCopySize,
398 return_data: SymReturnData,
399) -> Result<(), SymbolicError> {
400 state.return_data = return_data;
401 state.copy_call_output_offset(cx, out_offset, out_size)?;
402 state.stack.push(SymExpr::one(cx))?;
403 Ok(())
404}
405
406pub(crate) fn storage_slots_abi_array(cx: &mut SymCx, slots: Vec<SymExpr>) -> SymbolicAbiValue {
407 SymbolicAbiValue::Array {
408 elements: slots
409 .into_iter()
410 .map(|slot| SymbolicAbiValue::FixedBytes { bytes: slot.into_bytes(cx), size: 32 })
411 .collect(),
412 }
413}
414
415pub(crate) fn push_ascii(cx: &mut SymCx, out: &mut Vec<SymExpr>, value: &str) {
416 out.extend(value.bytes().map(|byte| SymExpr::constant(cx, U256::from(byte))));
417}
418
419pub(crate) fn push_hex_word(cx: &mut SymCx, out: &mut Vec<SymExpr>, word: SymExpr) {
420 for byte in word.into_byte_exprs(cx) {
421 push_hex_byte(cx, out, byte);
422 }
423}
424
425pub(crate) fn push_hex_byte(cx: &mut SymCx, out: &mut Vec<SymExpr>, byte: SymExpr) {
426 let byte = byte.low_byte(cx);
427 let (high, low) = if let Some(value) = byte.as_const() {
428 (
429 SymExpr::constant(cx, U256::from(value.to::<u8>() >> 4)),
430 SymExpr::constant(cx, U256::from(value.to::<u8>() & 0x0f)),
431 )
432 } else {
433 let expr = byte;
434 let shift = SymExpr::constant(cx, U256::from(4));
435 let mask = SymExpr::constant(cx, U256::from(0x0f));
436 (
437 SymExpr::binop(cx, SymBinOp::Shr, expr.clone(), shift),
438 SymExpr::binop(cx, SymBinOp::And, expr, mask),
439 )
440 };
441 out.push(hex_nibble_ascii(cx, high));
442 out.push(hex_nibble_ascii(cx, low));
443}
444
445pub(crate) fn hex_nibble_ascii(cx: &mut SymCx, nibble: SymExpr) -> SymExpr {
446 let nibble = nibble.low_byte(cx);
447 if let Some(value) = nibble.as_const() {
448 let nibble = value.to::<u8>() & 0x0f;
449 let byte = if nibble < 10 { b'0' + nibble } else { b'a' + (nibble - 10) };
450 SymExpr::constant(cx, U256::from(byte))
451 } else {
452 let ten = SymExpr::constant(cx, U256::from(10));
453 let condition = SymBoolExpr::cmp(cx, SymCmpOp::Ult, nibble.clone(), ten);
454 let zero = SymExpr::constant(cx, U256::from(b'0'));
455 let digit = SymExpr::binop(cx, SymBinOp::Add, nibble.clone(), zero);
456 let alpha_base = SymExpr::constant(cx, U256::from(b'a' - 10));
457 let alpha = SymExpr::binop(cx, SymBinOp::Add, nibble, alpha_base);
458 SymExpr::ite(cx, condition, digit, alpha)
459 }
460}
461
462pub(crate) fn read_abi_word_arg(
463 cx: &mut SymCx,
464 memory: &SymMemory,
465 args_offset: usize,
466 index: usize,
467) -> Result<SymExpr, SymbolicError> {
468 memory.load_word(cx, args_offset + index * 32)
469}
470
471pub(crate) fn read_abi_concrete_word_arg(
472 cx: &mut SymCx,
473 memory: &SymMemory,
474 args_offset: usize,
475 index: usize,
476 reason: &'static str,
477) -> Result<U256, SymbolicError> {
478 read_abi_word_arg(cx, memory, args_offset, index)?.as_const_or(reason)
479}
480
481pub(crate) fn read_abi_constrained_word_arg(
482 cx: &mut SymCx,
483 state: &PathState,
484 args_offset: usize,
485 index: usize,
486 reason: &'static str,
487) -> Result<U256, SymbolicError> {
488 let word = read_abi_word_arg(cx, &state.memory, args_offset, index)?;
489 state.expect_constrained_word(cx, word, reason)
490}
491
492pub(crate) fn read_abi_constrained_address_arg(
493 cx: &mut SymCx,
494 state: &PathState,
495 args_offset: usize,
496 index: usize,
497 reason: &'static str,
498) -> Result<Address, SymbolicError> {
499 Ok(word_to_address(read_abi_constrained_word_arg(cx, state, args_offset, index, reason)?))
500}
501
502pub(crate) fn read_abi_address_or_symbolic_slot_arg(
503 cx: &mut SymCx,
504 state: &mut PathState,
505 args_offset: usize,
506 index: usize,
507) -> Result<Address, SymbolicError> {
508 let word = read_abi_word_arg(cx, &state.memory, args_offset, index)?;
509 Ok(state.address_or_symbolic_slot(cx, word))
510}
511
512pub(crate) fn read_abi_address_word_or_symbolic_slot_arg(
513 cx: &mut SymCx,
514 state: &mut PathState,
515 args_offset: usize,
516 index: usize,
517) -> Result<(Address, SymExpr), SymbolicError> {
518 let word = read_abi_word_arg(cx, &state.memory, args_offset, index)?;
519 let address = state.address_or_symbolic_slot(cx, word.clone());
520 Ok((address, word))
521}
522
523pub(crate) fn read_abi_address_arg(
524 cx: &mut SymCx,
525 memory: &SymMemory,
526 args_offset: usize,
527 index: usize,
528 reason: &'static str,
529) -> Result<Address, SymbolicError> {
530 Ok(word_to_address(read_abi_concrete_word_arg(cx, memory, args_offset, index, reason)?))
531}
532
533pub(crate) fn read_abi_bool_arg(
534 cx: &mut SymCx,
535 memory: &SymMemory,
536 args_offset: usize,
537 index: usize,
538 reason: &'static str,
539) -> Result<bool, SymbolicError> {
540 Ok(!read_abi_concrete_word_arg(cx, memory, args_offset, index, reason)?.is_zero())
541}
542
543pub(crate) fn read_abi_u64_arg(
544 cx: &mut SymCx,
545 memory: &SymMemory,
546 args_offset: usize,
547 index: usize,
548 reason: &'static str,
549) -> Result<u64, SymbolicError> {
550 let value = read_abi_concrete_word_arg(cx, memory, args_offset, index, reason)?;
551 u64::try_from(value).map_err(|_| SymbolicError::Unsupported(reason))
552}
553
554pub(crate) fn read_abi_u32_arg(
555 cx: &mut SymCx,
556 memory: &SymMemory,
557 args_offset: usize,
558 index: usize,
559 reason: &'static str,
560) -> Result<u32, SymbolicError> {
561 let value = read_abi_concrete_word_arg(cx, memory, args_offset, index, reason)?;
562 u32::try_from(value).map_err(|_| SymbolicError::Unsupported(reason))
563}
564
565pub(crate) fn read_abi_bytes4_words_arg(
566 cx: &mut SymCx,
567 memory: &SymMemory,
568 args_offset: usize,
569 index: usize,
570) -> Vec<SymExpr> {
571 memory.read_byte_exprs(cx, args_offset + index * 32, 4)
572}
573
574pub(crate) fn read_abi_dynamic_bytes_arg(
575 cx: &mut SymCx,
576 memory: &SymMemory,
577 args_offset: usize,
578 index: usize,
579 reason: &'static str,
580) -> Result<Vec<u8>, SymbolicError> {
581 let offset =
582 usize::try_from(read_abi_concrete_word_arg(cx, memory, args_offset, index, reason)?)
583 .map_err(|_| SymbolicError::Unsupported(reason))?;
584 let len_offset = args_offset.checked_add(offset).ok_or(SymbolicError::Unsupported(reason))?;
585 let data_offset = len_offset.checked_add(32).ok_or(SymbolicError::Unsupported(reason))?;
586 let len = memory.load_word(cx, len_offset)?.as_usize_or(reason)?;
587 memory.read_concrete(cx, data_offset, len)
588}
589
590pub(crate) fn read_abi_symbolic_dynamic_bytes_arg(
591 cx: &mut SymCx,
592 state: &PathState,
593 args_offset: usize,
594 index: usize,
595 max_len: usize,
596 reason: &'static str,
597) -> Result<SymBytes, SymbolicError> {
598 let offset = read_abi_word_arg(cx, &state.memory, args_offset, index)?;
599 let offset = state.expect_constrained_usize(cx, offset, reason)?;
600 let len_offset = args_offset.checked_add(offset).ok_or(SymbolicError::Unsupported(reason))?;
601 let len = state.memory.load_word(cx, len_offset)?;
602 let len = state.expect_constrained_usize(cx, len, reason)?;
603 if len > max_len {
604 return Err(SymbolicError::Unsupported(reason));
605 }
606 let data_offset = len_offset.checked_add(32).ok_or(SymbolicError::Unsupported(reason))?;
607 Ok(state.memory.read_bytes(cx, data_offset, len))
608}
609
610pub(crate) fn read_abi_symbolic_dynamic_byte_exprs_arg(
611 cx: &mut SymCx,
612 state: &PathState,
613 args_offset: usize,
614 index: usize,
615 max_len: usize,
616 reason: &'static str,
617) -> Result<Vec<SymExpr>, SymbolicError> {
618 Ok(read_abi_symbolic_dynamic_bytes_arg(cx, state, args_offset, index, max_len, reason)?
619 .materialize(cx))
620}
621
622pub(crate) fn read_abi_dynamic_return_data_arg(
623 cx: &mut SymCx,
624 state: &PathState,
625 args_offset: usize,
626 index: usize,
627 max_len: usize,
628 reason: &'static str,
629) -> Result<SymReturnData, SymbolicError> {
630 let bytes =
631 read_abi_symbolic_dynamic_bytes_arg(cx, state, args_offset, index, max_len, reason)?;
632 Ok(SymReturnData::from_bytes(cx, bytes))
633}
634
635pub(crate) fn read_abi_symbolic_dynamic_bytes_array_arg(
636 cx: &mut SymCx,
637 state: &PathState,
638 args_offset: usize,
639 index: usize,
640 max_array_len: usize,
641 max_bytes_len: usize,
642) -> Result<Vec<SymReturnData>, SymbolicError> {
643 let offset = read_abi_word_arg(cx, &state.memory, args_offset, index)?;
644 let offset =
645 state.expect_constrained_usize(cx, offset, "symbolic vm.mockCalls returns offset")?;
646 let array_offset = args_offset
647 .checked_add(offset)
648 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns offset"))?;
649 let array_data_offset = array_offset
650 .checked_add(32)
651 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns offset"))?;
652 let len = state.memory.load_word(cx, array_offset)?;
653 let len = state.expect_constrained_usize(cx, len, "symbolic vm.mockCalls returns length")?;
654 if len > max_array_len {
655 return Err(SymbolicError::Unsupported("symbolic vm.mockCalls returns length"));
656 }
657
658 let mut values = Vec::with_capacity(len);
659 for value_idx in 0..len {
660 let head_offset = array_offset
661 .checked_add(32)
662 .and_then(|offset| offset.checked_add(value_idx.saturating_mul(32)))
663 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
664 let value_offset = state.memory.load_word(cx, head_offset)?;
665 let value_offset = state.expect_constrained_usize(
666 cx,
667 value_offset,
668 "symbolic vm.mockCalls returns element offset",
669 )?;
670 let len_offset = array_data_offset
671 .checked_add(value_offset)
672 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
673 let value_len = state.memory.load_word(cx, len_offset)?;
674 let value_len = state.expect_constrained_usize(
675 cx,
676 value_len,
677 "symbolic vm.mockCalls returns element length",
678 )?;
679 if value_len > max_bytes_len {
680 return Err(SymbolicError::Unsupported("symbolic vm.mockCalls returns element length"));
681 }
682 let data_offset = len_offset
683 .checked_add(32)
684 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
685 let bytes = state.memory.read_bytes(cx, data_offset, value_len);
686 values.push(SymReturnData::from_bytes(cx, bytes));
687 }
688
689 Ok(values)
690}
691
692pub(crate) fn read_abi_string_arg(
693 cx: &mut SymCx,
694 memory: &SymMemory,
695 args_offset: usize,
696 index: usize,
697 reason: &'static str,
698) -> Result<String, SymbolicError> {
699 String::from_utf8(read_abi_dynamic_bytes_arg(cx, memory, args_offset, index, reason)?)
700 .map_err(|_| SymbolicError::Unsupported(reason))
701}
702
703pub(crate) fn decode_cheatcode_args(
704 cx: &mut SymCx,
705 state: &PathState,
706 in_offset: usize,
707 in_size: usize,
708 tys: Vec<DynSolType>,
709) -> Result<Vec<DynSolValue>, SymbolicError> {
710 let data = state.memory.read_concrete(cx, in_offset + 4, in_size.saturating_sub(4))?;
711 let value = DynSolType::Tuple(tys)
712 .abi_decode_sequence(&data)
713 .map_err(|_| SymbolicError::Unsupported("symbolic cheatcode ABI decode"))?;
714 let DynSolValue::Tuple(values) = value else {
715 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
716 };
717 Ok(values)
718}
719
720pub(crate) const fn selector_has_string_reason(selector: [u8; 4]) -> bool {
721 matches!(
722 selector,
723 assertEq_15Call::SELECTOR
724 | assertEq_17Call::SELECTOR
725 | assertEq_19Call::SELECTOR
726 | assertEq_21Call::SELECTOR
727 | assertEq_23Call::SELECTOR
728 | assertEq_25Call::SELECTOR
729 | assertEq_27Call::SELECTOR
730 | assertNotEq_15Call::SELECTOR
731 | assertNotEq_17Call::SELECTOR
732 | assertNotEq_19Call::SELECTOR
733 | assertNotEq_21Call::SELECTOR
734 | assertNotEq_23Call::SELECTOR
735 | assertNotEq_25Call::SELECTOR
736 | assertNotEq_27Call::SELECTOR
737 | assertEqDecimal_1Call::SELECTOR
738 | assertEqDecimal_3Call::SELECTOR
739 )
740}
741
742pub(crate) const fn array_assertion_element_type(
743 selector: [u8; 4],
744) -> Result<DynSolType, SymbolicError> {
745 match selector {
746 assertEq_14Call::SELECTOR
747 | assertEq_15Call::SELECTOR
748 | assertNotEq_14Call::SELECTOR
749 | assertNotEq_15Call::SELECTOR => Ok(DynSolType::Bool),
750 assertEq_16Call::SELECTOR
751 | assertEq_17Call::SELECTOR
752 | assertNotEq_16Call::SELECTOR
753 | assertNotEq_17Call::SELECTOR => Ok(DynSolType::Uint(256)),
754 assertEq_18Call::SELECTOR
755 | assertEq_19Call::SELECTOR
756 | assertNotEq_18Call::SELECTOR
757 | assertNotEq_19Call::SELECTOR => Ok(DynSolType::Int(256)),
758 assertEq_20Call::SELECTOR
759 | assertEq_21Call::SELECTOR
760 | assertNotEq_20Call::SELECTOR
761 | assertNotEq_21Call::SELECTOR => Ok(DynSolType::Address),
762 assertEq_22Call::SELECTOR
763 | assertEq_23Call::SELECTOR
764 | assertNotEq_22Call::SELECTOR
765 | assertNotEq_23Call::SELECTOR => Ok(DynSolType::FixedBytes(32)),
766 assertEq_24Call::SELECTOR
767 | assertEq_25Call::SELECTOR
768 | assertNotEq_24Call::SELECTOR
769 | assertNotEq_25Call::SELECTOR => Ok(DynSolType::String),
770 assertEq_26Call::SELECTOR
771 | assertEq_27Call::SELECTOR
772 | assertNotEq_26Call::SELECTOR
773 | assertNotEq_27Call::SELECTOR => Ok(DynSolType::Bytes),
774 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
775 }
776}
777
778pub(crate) fn dyn_string(value: &DynSolValue) -> Result<String, SymbolicError> {
779 match value {
780 DynSolValue::String(value) => Ok(value.clone()),
781 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
782 }
783}
784
785pub(crate) fn dyn_bytes(value: &DynSolValue) -> Result<Vec<u8>, SymbolicError> {
786 match value {
787 DynSolValue::Bytes(value) => Ok(value.clone()),
788 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
789 }
790}
791
792pub(crate) const fn dyn_bool(value: &DynSolValue) -> Result<bool, SymbolicError> {
793 match value {
794 DynSolValue::Bool(value) => Ok(*value),
795 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
796 }
797}
798
799pub(crate) const fn dyn_address(value: &DynSolValue) -> Result<Address, SymbolicError> {
800 match value {
801 DynSolValue::Address(value) => Ok(*value),
802 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
803 }
804}
805
806pub(crate) fn dyn_potential_revert(
807 cx: &mut SymCx,
808 value: &DynSolValue,
809) -> Result<ExpectedRevert, SymbolicError> {
810 let DynSolValue::Tuple(values) = value else {
811 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
812 };
813 let [reverter, partial_match, revert_data] = values.as_slice() else {
814 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
815 };
816
817 let reverter = dyn_address(reverter)?;
818 let reverter =
819 (reverter != Address::ZERO).then(|| SymExpr::constant(cx, address_word(reverter)));
820 let revert_data = SymBytes::concrete(cx, dyn_bytes(revert_data)?);
821 let data = if dyn_bool(partial_match)? {
822 ExpectedRevertData::prefix(revert_data)
823 } else {
824 ExpectedRevertData::exact(revert_data)
825 };
826 Ok(ExpectedRevert::new(data, reverter, 1))
827}
828
829pub(crate) fn dyn_potential_reverts(
830 cx: &mut SymCx,
831 value: &DynSolValue,
832) -> Result<Vec<ExpectedRevert>, SymbolicError> {
833 let DynSolValue::Array(values) = value else {
834 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
835 };
836 values.iter().map(|value| dyn_potential_revert(cx, value)).collect()
837}
838
839pub(crate) fn dyn_address_array(value: &DynSolValue) -> Result<Vec<Address>, SymbolicError> {
840 let DynSolValue::Array(values) = value else {
841 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
842 };
843 values.iter().map(dyn_address).collect()
844}
845
846pub(crate) fn dyn_bytes32_array(value: &DynSolValue) -> Result<Vec<B256>, SymbolicError> {
847 let DynSolValue::Array(values) = value else {
848 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
849 };
850 values
851 .iter()
852 .map(|value| match value {
853 DynSolValue::FixedBytes(bytes, 32) => Ok(*bytes),
854 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
855 })
856 .collect()
857}
858
859pub(crate) fn dyn_string_array(value: &DynSolValue) -> Result<Vec<String>, SymbolicError> {
860 let DynSolValue::Array(values) = value else {
861 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
862 };
863 values.iter().map(dyn_string).collect()
864}
865
866pub(crate) fn parse_env_array<F>(
867 value: &str,
868 delimiter: &str,
869 mut parser: F,
870) -> Result<DynSolValue, SymbolicError>
871where
872 F: FnMut(&str) -> Result<DynSolValue, SymbolicError>,
873{
874 if delimiter.is_empty() {
875 return Err(SymbolicError::Unsupported("symbolic env delimiter"));
876 }
877 value.split(delimiter).map(&mut parser).collect::<Result<Vec<_>, _>>().map(DynSolValue::Array)
878}
879
880pub(crate) fn parse_env_bool_value(value: &str) -> Result<DynSolValue, SymbolicError> {
881 Ok(DynSolValue::Bool(parse_env_bool(value)?))
882}
883
884pub(crate) fn parse_env_uint_value(value: &str) -> Result<DynSolValue, SymbolicError> {
885 Ok(DynSolValue::Uint(parse_env_uint(value)?, 256))
886}
887
888pub(crate) fn parse_env_int_value(value: &str) -> Result<DynSolValue, SymbolicError> {
889 Ok(DynSolValue::Int(I256::from_raw(parse_env_int(value)?), 256))
890}
891
892pub(crate) fn parse_env_address_value(value: &str) -> Result<DynSolValue, SymbolicError> {
893 Ok(DynSolValue::Address(parse_env_address(value)?))
894}
895
896pub(crate) fn parse_env_bytes32_value(value: &str) -> Result<DynSolValue, SymbolicError> {
897 Ok(DynSolValue::FixedBytes(B256::from(parse_env_bytes32(value)?.to_be_bytes::<32>()), 32))
898}
899
900pub(crate) fn parse_env_string_value(value: &str) -> Result<DynSolValue, SymbolicError> {
901 Ok(DynSolValue::String(value.to_string()))
902}
903
904pub(crate) fn parse_env_bytes_value(value: &str) -> Result<DynSolValue, SymbolicError> {
905 Ok(DynSolValue::Bytes(parse_env_bytes(value)?))
906}
907
908pub(crate) fn parse_env_uint(value: &str) -> Result<U256, SymbolicError> {
909 value.parse::<U256>().map_err(|_| SymbolicError::Unsupported("symbolic env uint parse"))
910}
911
912pub(crate) fn parse_env_int(value: &str) -> Result<U256, SymbolicError> {
913 if let Some(value) = value.strip_prefix('-') {
914 let magnitude = parse_env_uint(value)?;
915 Ok(U256::ZERO.wrapping_sub(magnitude))
916 } else {
917 parse_env_uint(value)
918 }
919}
920
921pub(crate) fn parse_env_bool(value: &str) -> Result<bool, SymbolicError> {
922 match value {
923 "true" | "1" | "TRUE" | "True" => Ok(true),
924 "false" | "0" | "FALSE" | "False" => Ok(false),
925 _ => Err(SymbolicError::Unsupported("symbolic env bool parse")),
926 }
927}
928
929pub(crate) fn parse_env_bytes(value: &str) -> Result<Vec<u8>, SymbolicError> {
930 let value = value.strip_prefix("0x").unwrap_or(value);
931 hex::decode(value).map_err(|_| SymbolicError::Unsupported("symbolic env bytes parse"))
932}
933
934pub(crate) fn parse_env_bytes32(value: &str) -> Result<U256, SymbolicError> {
935 let bytes = parse_env_bytes(value)?;
936 if bytes.len() != 32 {
937 return Err(SymbolicError::Unsupported("symbolic env bytes32 parse"));
938 }
939 Ok(U256::from_be_slice(&bytes))
940}
941
942pub(crate) fn parse_env_address(value: &str) -> Result<Address, SymbolicError> {
943 value.parse::<Address>().map_err(|_| SymbolicError::Unsupported("symbolic env address parse"))
944}
945
946pub(crate) fn private_key_signer(private_key: U256) -> Result<PrivateKeySigner, SymbolicError> {
947 private_key_from_u256(private_key)
948 .map_err(|_| SymbolicError::Unsupported("symbolic private key parse"))
949}
950
951pub(crate) fn private_key_address(private_key: U256) -> Result<Address, SymbolicError> {
952 Ok(private_key_signer(private_key)?.address())
953}
954
955pub(crate) fn sign_hash_words(
956 cx: &mut SymCx,
957 private_key: U256,
958 digest: U256,
959) -> Result<Vec<SymExpr>, SymbolicError> {
960 let signer = private_key_signer(private_key)?;
961 let digest = B256::from(digest.to_be_bytes::<32>());
962 let sig = signer
963 .sign_hash_sync(&digest)
964 .map_err(|_| SymbolicError::Unsupported("symbolic vm.sign"))?;
965 Ok(vec![
966 SymExpr::constant(cx, U256::from(sig.v_byte())),
967 SymExpr::constant(cx, sig.r()),
968 SymExpr::constant(cx, sig.s()),
969 ])
970}
971
972pub(crate) fn sign_compact_hash_words(
973 cx: &mut SymCx,
974 private_key: U256,
975 digest: U256,
976) -> Result<Vec<SymExpr>, SymbolicError> {
977 let signer = private_key_signer(private_key)?;
978 let digest = B256::from(digest.to_be_bytes::<32>());
979 let sig = signer
980 .sign_hash_sync(&digest)
981 .map_err(|_| SymbolicError::Unsupported("symbolic vm.signCompact"))?;
982 let y_parity = U256::from(sig.v() as u64) << 255;
983 Ok(vec![SymExpr::constant(cx, sig.r()), SymExpr::constant(cx, sig.s() | y_parity)])
984}
985
986pub(crate) fn derive_private_key<W: Wordlist>(
987 mnemonic: &str,
988 path: &str,
989 index: u32,
990) -> Result<U256, SymbolicError> {
991 foundry_common::wallet::derive_private_key::<W>(mnemonic, path, index)
992 .map_err(|_| SymbolicError::Unsupported("symbolic vm.deriveKey"))
993}
994
995pub(crate) fn derive_private_key_with_language(
996 mnemonic: &str,
997 path: &str,
998 index: u32,
999 language: &str,
1000) -> Result<U256, SymbolicError> {
1001 foundry_common::wallet::derive_private_key_with_language(mnemonic, path, index, language)
1002 .map_err(|_| SymbolicError::Unsupported("symbolic vm.deriveKey language"))
1003}
1004
1005pub(crate) fn artifact_json_path(path: &str) -> PathBuf {
1006 if path.ends_with(".json") {
1007 return PathBuf::from(path);
1008 }
1009
1010 let mut parts = path.split(':');
1011 let first = parts.next().unwrap_or_default();
1012 let second = parts.next();
1013
1014 if first.contains('.') {
1015 let file = Path::new(first);
1016 let contract = second
1017 .map(str::to_string)
1018 .or_else(|| file.file_stem().map(|stem| stem.to_string_lossy().to_string()))
1019 .unwrap_or_else(|| first.to_string());
1020 PathBuf::from("out").join(first).join(format!("{contract}.json"))
1021 } else {
1022 let contract = first;
1023 PathBuf::from("out").join(format!("{contract}.sol")).join(format!("{contract}.json"))
1024 }
1025}
1026
1027pub(crate) fn artifact_json_fallback_paths(path: &str) -> Vec<PathBuf> {
1029 if path.ends_with(".json") {
1030 return Vec::new();
1031 }
1032
1033 let mut parts = path.split(':');
1034 let first = parts.next().unwrap_or_default();
1035 let second = parts.next();
1036 let Some(file_name) = first.rsplit(['/', '\\']).find(|part| !part.is_empty()) else {
1037 return Vec::new();
1038 };
1039
1040 if !first.contains('/') && !first.contains('\\') {
1041 return Vec::new();
1042 }
1043
1044 let contract = second.map(str::to_string).unwrap_or_else(|| {
1045 file_name.rsplit_once('.').map(|(stem, _)| stem).unwrap_or(file_name).to_string()
1046 });
1047 vec![PathBuf::from("out").join(file_name).join(format!("{contract}.json"))]
1048}
1049
1050pub(crate) fn artifact_code(path: &str, deployed: bool) -> Result<Vec<u8>, SymbolicError> {
1051 let mut data = None;
1052 for path in std::iter::once(artifact_json_path(path)).chain(artifact_json_fallback_paths(path))
1053 {
1054 if let Ok(contents) = std::fs::read_to_string(path) {
1055 data = Some(contents);
1056 break;
1057 }
1058 }
1059 let data = data.ok_or(SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1060 let artifact: serde_json::Value = serde_json::from_str(&data)
1061 .map_err(|_| SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1062 let key = if deployed { "deployedBytecode" } else { "bytecode" };
1063 let object = artifact
1064 .get(key)
1065 .and_then(|value| value.get("object").or(Some(value)))
1066 .and_then(serde_json::Value::as_str)
1067 .ok_or(SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1068 hex::decode(object).map_err(|_| SymbolicError::Unsupported("symbolic vm.getCode artifact"))
1069}