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