1use foundry_common::wallet::private_key_from_u256;
2
3use super::*;
4
5pub(crate) fn foundry_cheatcode_min_input_size(selector: [u8; 4]) -> Option<usize> {
7 if selector_in(
8 selector,
9 &[
10 "recordLogs()",
11 "record()",
12 "stopRecord()",
13 "assumeNoRevert()",
14 "getRecordedLogs()",
15 "getRecordedLogsJson()",
16 "expectRevert()",
17 "expectEmit()",
18 "expectEmitAnonymous()",
19 "clearMockedCalls()",
20 "stopPrank()",
21 "readCallers()",
22 "getWallets()",
23 "snapshot()",
24 "snapshotState()",
25 "deleteSnapshots()",
26 "deleteStateSnapshots()",
27 "activeFork()",
28 "getChainId()",
29 "getBlobhashes()",
30 "getBlobBaseFee()",
31 "getBlockNumber()",
32 "getBlockTimestamp()",
33 "pauseGasMetering()",
34 "resumeGasMetering()",
35 "resetGasMetering()",
36 "lastCallGas()",
37 "stopExpectSafeMemory()",
38 "stopSnapshotGas()",
39 "getEvmVersion()",
40 "getFoundryVersion()",
41 "projectRoot()",
42 "unixTime()",
43 "noAccessList()",
44 "randomUint()",
45 "randomInt()",
46 "randomAddress()",
47 "randomBool()",
48 "randomBytes4()",
49 "randomBytes8()",
50 ],
51 ) {
52 return Some(abi_static_input_size(0));
53 }
54
55 if selector_in(
56 selector,
57 &[
58 "assume(bool)",
59 "skip(bool)",
60 "skip(bool,string)",
61 "assumeNoRevert((address,bool,bytes))",
62 "assumeNoRevert((address,bool,bytes)[])",
63 "accesses(address)",
64 "expectRevert(bytes4)",
65 "expectRevert(address)",
66 "expectRevert(uint64)",
67 "expectPartialRevert(bytes4)",
68 "expectEmit(address)",
69 "expectEmit(uint64)",
70 "expectEmitAnonymous(address)",
71 "prank(address)",
72 "startPrank(address)",
73 "addr(uint256)",
74 "rememberKey(uint256)",
75 "toString(address)",
76 "toString(bytes)",
77 "toString(bytes32)",
78 "toString(bool)",
79 "toString(uint256)",
80 "toString(int256)",
81 "parseBytes(string)",
82 "parseAddress(string)",
83 "parseUint(string)",
84 "parseInt(string)",
85 "parseBytes32(string)",
86 "parseBool(string)",
87 "toLowercase(string)",
88 "toUppercase(string)",
89 "trim(string)",
90 "toBase64(bytes)",
91 "toBase64(string)",
92 "toBase64URL(bytes)",
93 "toBase64URL(string)",
94 "breakpoint(string)",
95 "setEvmVersion(string)",
96 "sleep(uint256)",
97 "accessList((address,bytes32[])[])",
98 "getNonce(address)",
99 "resetNonce(address)",
100 "allowCheatcodes(address)",
101 "makePersistent(address)",
102 "makePersistent(address[])",
103 "revokePersistent(address)",
104 "revokePersistent(address[])",
105 "isPersistent(address)",
106 "selectFork(uint256)",
107 "createFork(string)",
108 "createSelectFork(string)",
109 "rollFork(uint256)",
110 "rollFork(bytes32)",
111 "revertTo(uint256)",
112 "revertToState(uint256)",
113 "revertToAndDelete(uint256)",
114 "revertToStateAndDelete(uint256)",
115 "deleteSnapshot(uint256)",
116 "deleteStateSnapshot(uint256)",
117 "warp(uint256)",
118 "roll(uint256)",
119 "prevrandao(bytes32)",
120 "prevrandao(uint256)",
121 "fee(uint256)",
122 "blobBaseFee(uint256)",
123 "chainId(uint256)",
124 "difficulty(uint256)",
125 "coinbase(address)",
126 "txGasPrice(uint256)",
127 "getLabel(address)",
128 "snapshotGasLastCall(string)",
129 "startSnapshotGas(string)",
130 "stopSnapshotGas(string)",
131 "cool(address)",
132 "isContext(uint8)",
133 "assertTrue(bool)",
134 "assertTrue(bool,string)",
135 "assertFalse(bool)",
136 "assertFalse(bool,string)",
137 "randomUint(uint256)",
138 "randomInt(uint256)",
139 "randomBytes(uint256)",
140 ],
141 ) {
142 return Some(abi_static_input_size(1));
143 }
144
145 if selector_in(
146 selector,
147 &[
148 "expectRevert(bytes4,address)",
149 "expectRevert(bytes4,uint64)",
150 "expectRevert(address,uint64)",
151 "expectPartialRevert(bytes4,address)",
152 "expectEmit(address,uint64)",
153 "prank(address,address)",
154 "prank(address,bool)",
155 "startPrank(address,address)",
156 "startPrank(address,bool)",
157 "sign(uint256,bytes32)",
158 "signCompact(uint256,bytes32)",
159 "deriveKey(string,uint32)",
160 "split(string,string)",
161 "indexOf(string,string)",
162 "contains(string,string)",
163 "breakpoint(string,bool)",
164 "expectSafeMemory(uint64,uint64)",
165 "expectSafeMemoryCall(uint64,uint64)",
166 "load(address,bytes32)",
167 "makePersistent(address,address)",
168 "computeCreateAddress(address,uint256)",
169 "computeCreate2Address(bytes32,bytes32)",
170 "setBlockhash(uint256,bytes32)",
171 "deal(address,uint256)",
172 "setNonce(address,uint64)",
173 "setNonceUnsafe(address,uint64)",
174 "createFork(string,uint256)",
175 "createFork(string,bytes32)",
176 "createSelectFork(string,uint256)",
177 "createSelectFork(string,bytes32)",
178 "rollFork(uint256,uint256)",
179 "rollFork(uint256,bytes32)",
180 "label(address,string)",
181 "snapshotValue(string,uint256)",
182 "snapshotGasLastCall(string,string)",
183 "startSnapshotGas(string,string)",
184 "stopSnapshotGas(string,string)",
185 "warmSlot(address,bytes32)",
186 "coolSlot(address,bytes32)",
187 "assertEq(uint256,uint256)",
188 "assertEq(uint256,uint256,string)",
189 "assertEq(int256,int256)",
190 "assertEq(int256,int256,string)",
191 "assertEq(address,address)",
192 "assertEq(address,address,string)",
193 "assertEq(bytes32,bytes32)",
194 "assertEq(bytes32,bytes32,string)",
195 "assertEq(bool,bool)",
196 "assertEq(bool,bool,string)",
197 "assertNotEq(uint256,uint256)",
198 "assertNotEq(uint256,uint256,string)",
199 "assertNotEq(int256,int256)",
200 "assertNotEq(int256,int256,string)",
201 "assertNotEq(address,address)",
202 "assertNotEq(address,address,string)",
203 "assertNotEq(bytes32,bytes32)",
204 "assertNotEq(bytes32,bytes32,string)",
205 "assertNotEq(bool,bool)",
206 "assertNotEq(bool,bool,string)",
207 "assertLt(uint256,uint256)",
208 "assertLt(uint256,uint256,string)",
209 "assertLe(uint256,uint256)",
210 "assertLe(uint256,uint256,string)",
211 "assertGt(uint256,uint256)",
212 "assertGt(uint256,uint256,string)",
213 "assertGe(uint256,uint256)",
214 "assertGe(uint256,uint256,string)",
215 "assertLt(int256,int256)",
216 "assertLt(int256,int256,string)",
217 "assertGt(int256,int256)",
218 "assertGt(int256,int256,string)",
219 "assertLe(int256,int256)",
220 "assertLe(int256,int256,string)",
221 "assertGe(int256,int256)",
222 "assertGe(int256,int256,string)",
223 "assertEq(bool[],bool[])",
224 "assertEq(bool[],bool[],string)",
225 "assertEq(uint256[],uint256[])",
226 "assertEq(uint256[],uint256[],string)",
227 "assertEq(int256[],int256[])",
228 "assertEq(int256[],int256[],string)",
229 "assertEq(address[],address[])",
230 "assertEq(address[],address[],string)",
231 "assertEq(bytes32[],bytes32[])",
232 "assertEq(bytes32[],bytes32[],string)",
233 "assertEq(string[],string[])",
234 "assertEq(string[],string[],string)",
235 "assertEq(bytes[],bytes[])",
236 "assertEq(bytes[],bytes[],string)",
237 "assertNotEq(bool[],bool[])",
238 "assertNotEq(bool[],bool[],string)",
239 "assertNotEq(uint256[],uint256[])",
240 "assertNotEq(uint256[],uint256[],string)",
241 "assertNotEq(int256[],int256[])",
242 "assertNotEq(int256[],int256[],string)",
243 "assertNotEq(address[],address[])",
244 "assertNotEq(address[],address[],string)",
245 "assertNotEq(bytes32[],bytes32[])",
246 "assertNotEq(bytes32[],bytes32[],string)",
247 "assertNotEq(string[],string[])",
248 "assertNotEq(string[],string[],string)",
249 "assertNotEq(bytes[],bytes[])",
250 "assertNotEq(bytes[],bytes[],string)",
251 "randomUint(uint256,uint256)",
252 ],
253 ) {
254 return Some(abi_static_input_size(2));
255 }
256
257 if selector_in(
258 selector,
259 &[
260 "expectRevert(bytes4,address,uint64)",
261 "deriveKey(string,string,uint32)",
262 "deriveKey(string,uint32,string)",
263 "rememberKeys(string,string,uint32)",
264 "store(address,bytes32,bytes32)",
265 "makePersistent(address,address,address)",
266 "snapshotValue(string,string,uint256)",
267 "replace(string,string,string)",
268 "assertEqDecimal(uint256,uint256,uint256)",
269 "assertEqDecimal(int256,int256,uint256)",
270 "computeCreate2Address(bytes32,bytes32,address)",
271 "bound(uint256,uint256,uint256)",
272 "bound(int256,int256,int256)",
273 ],
274 ) {
275 return Some(abi_static_input_size(3));
276 }
277
278 if selector_in(
279 selector,
280 &[
281 "expectEmit(bool,bool,bool,bool)",
282 "deriveKey(string,string,uint32,string)",
283 "rememberKeys(string,string,string,uint32)",
284 "assertEqDecimal(uint256,uint256,uint256,string)",
285 "assertEqDecimal(int256,int256,uint256,string)",
286 ],
287 ) {
288 return Some(abi_static_input_size(4));
289 }
290 if selector_in(selector, &["expectEmitAnonymous(bool,bool,bool,bool,bool)"]) {
291 return Some(abi_static_input_size(5));
292 }
293 if selector_in(
294 selector,
295 &["expectEmit(bool,bool,bool,bool,address)", "expectEmit(bool,bool,bool,bool,uint64)"],
296 ) {
297 return Some(abi_static_input_size(5));
298 }
299 if selector_in(
300 selector,
301 &[
302 "expectEmit(bool,bool,bool,bool,address,uint64)",
303 "expectEmitAnonymous(bool,bool,bool,bool,bool,address)",
304 ],
305 ) {
306 return Some(abi_static_input_size(6));
307 }
308
309 None
310}
311
312pub(crate) fn symbolic_vm_cheatcode_min_input_size(selector: [u8; 4]) -> Option<usize> {
314 if selector_in(
315 selector,
316 &[
317 "createUint256(string)",
318 "createInt256(string)",
319 "createBytes32(string)",
320 "createAddress(string)",
321 "createBool(string)",
322 "createBytes(string)",
323 "createString(string)",
324 "createBytes4(string)",
325 "createCalldata(string)",
326 "snapshotState()",
327 ],
328 ) || (8..=256).step_by(8).any(|bits| {
329 selector == selector_for(&format!("createUint{bits}(string)"))
330 || selector == selector_for(&format!("createInt{bits}(string)"))
331 }) || (1..=32).any(|bytes| selector == selector_for(&format!("createBytes{bytes}(string)")))
332 {
333 return Some(abi_static_input_size(0));
334 }
335
336 if selector_in(
337 selector,
338 &[
339 "enableSymbolicStorage(address)",
340 "setArbitraryStorage(address)",
341 "snapshotStorage(address)",
342 ],
343 ) {
344 return Some(abi_static_input_size(1));
345 }
346 if selector_in(
347 selector,
348 &[
349 "createUint(uint256,string)",
350 "createInt(uint256,string)",
351 "createBytes(uint256,string)",
352 "createString(uint256,string)",
353 ],
354 ) {
355 return Some(abi_static_input_size(1));
356 }
357
358 None
359}
360
361pub(crate) const fn abi_static_input_size(words: usize) -> usize {
363 4 + words * 32
364}
365
366pub(crate) fn selector_in(selector: [u8; 4], signatures: &[&str]) -> bool {
368 signatures.iter().any(|signature| selector == selector_for(signature))
369}
370
371pub(crate) fn abi_bytes_return(bytes: Vec<SymWord>) -> SymReturnData {
373 abi_bytes_return_with_len(SymWord::Concrete(U256::from(bytes.len())), bytes)
374}
375
376pub(crate) fn abi_bytes_return_with_len(len: SymWord, bytes: Vec<SymWord>) -> SymReturnData {
378 let mut out = word_bytes(SymWord::Concrete(U256::from(32)));
379 out.extend(word_bytes(len));
380 out.extend(bytes.iter().cloned());
381 out.resize(64 + bytes.len().next_multiple_of(32), SymWord::zero());
382 SymReturnData::from_symbolic_bytes(out)
383}
384
385pub(crate) fn abi_concrete_bytes_return(bytes: impl IntoIterator<Item = u8>) -> SymReturnData {
387 abi_bytes_return(bytes.into_iter().map(|byte| SymWord::Concrete(U256::from(byte))).collect())
388}
389
390pub(crate) fn abi_concrete_value_return(value: DynSolValue) -> SymReturnData {
392 SymReturnData::from_symbolic_bytes(
393 value.abi_encode().into_iter().map(|byte| SymWord::Concrete(U256::from(byte))).collect(),
394 )
395}
396
397pub(crate) fn recorded_logs_return_data(logs: Vec<SymbolicLog>) -> SymReturnData {
399 let value = SymbolicAbiValue::Array {
400 elements: logs
401 .into_iter()
402 .map(|log| {
403 let topics = log
404 .topics
405 .into_iter()
406 .map(|topic| SymbolicAbiValue::FixedBytes {
407 bytes: word_bytes(topic),
408 size: 32,
409 })
410 .collect();
411 SymbolicAbiValue::Tuple {
412 elements: vec![
413 SymbolicAbiValue::Array { elements: topics },
414 SymbolicAbiValue::Bytes { len: log.data_len, bytes: log.data },
415 SymbolicAbiValue::Address {
416 word: SymWord::Concrete(address_word(log.emitter)),
417 },
418 ],
419 }
420 })
421 .collect(),
422 };
423 SymReturnData::from_symbolic_bytes(encode_sequence(std::iter::once(&value)))
424}
425
426pub(crate) fn recorded_logs_json_return_data(
428 logs: Vec<SymbolicLog>,
429) -> Result<SymReturnData, SymbolicError> {
430 let mut bytes = Vec::new();
431 push_ascii(&mut bytes, "[");
432 for (log_idx, log) in logs.into_iter().enumerate() {
433 if log_idx > 0 {
434 push_ascii(&mut bytes, ",");
435 }
436 push_ascii(&mut bytes, "{\"topics\":[");
437 for (topic_idx, topic) in log.topics.into_iter().enumerate() {
438 if topic_idx > 0 {
439 push_ascii(&mut bytes, ",");
440 }
441 push_ascii(&mut bytes, "\"0x");
442 push_hex_word(&mut bytes, topic);
443 push_ascii(&mut bytes, "\"");
444 }
445 push_ascii(&mut bytes, "],\"data\":\"0x");
446
447 let len = log
448 .data_len
449 .into_concrete("symbolic vm.getRecordedLogsJson data length")
450 .and_then(|len| {
451 if len > U256::from(usize::MAX) {
452 Err(SymbolicError::Unsupported("symbolic vm.getRecordedLogsJson data length"))
453 } else {
454 Ok(len.to::<usize>())
455 }
456 })?;
457 if len > log.data.len() {
458 return Err(SymbolicError::Unsupported("symbolic vm.getRecordedLogsJson data length"));
459 }
460 for byte in log.data.into_iter().take(len) {
461 push_hex_byte(&mut bytes, byte);
462 }
463
464 push_ascii(&mut bytes, "\",\"emitter\":\"");
465 push_ascii(&mut bytes, &format!("{}", log.emitter));
466 push_ascii(&mut bytes, "\"}");
467 }
468 push_ascii(&mut bytes, "]");
469 Ok(abi_bytes_return(bytes))
470}
471
472pub(crate) fn accesses_return_data(
474 record: Option<&AccessRecord>,
475 target: Address,
476) -> SymReturnData {
477 let reads = record.and_then(|record| record.reads.get(&target)).cloned().unwrap_or_default();
478 let writes = record.and_then(|record| record.writes.get(&target)).cloned().unwrap_or_default();
479 let values = [storage_slots_abi_array(reads), storage_slots_abi_array(writes)];
480 SymReturnData::from_symbolic_bytes(encode_sequence(values.iter()))
481}
482
483pub(crate) fn complete_cheatcode_call(
485 state: &mut PathState,
486 out_offset: SymWord,
487 out_size: &BoundedCopySize,
488 return_data: SymReturnData,
489) -> Result<(), SymbolicError> {
490 state.return_data = return_data;
491 let return_data = state.return_data.clone();
492 state.memory.copy_call_output_offset(out_offset, out_size, &return_data)?;
493 state.stack.push(SymWord::Concrete(U256::from(1)))?;
494 Ok(())
495}
496
497pub(crate) fn storage_slots_abi_array(slots: Vec<SymWord>) -> SymbolicAbiValue {
499 SymbolicAbiValue::Array {
500 elements: slots
501 .into_iter()
502 .map(|slot| SymbolicAbiValue::FixedBytes { bytes: word_bytes(slot), size: 32 })
503 .collect(),
504 }
505}
506
507pub(crate) fn push_ascii(out: &mut Vec<SymWord>, value: &str) {
509 out.extend(value.bytes().map(|byte| SymWord::Concrete(U256::from(byte))));
510}
511
512pub(crate) fn push_hex_word(out: &mut Vec<SymWord>, word: SymWord) {
514 for byte in word_bytes(word) {
515 push_hex_byte(out, byte);
516 }
517}
518
519pub(crate) fn push_hex_byte(out: &mut Vec<SymWord>, byte: SymWord) {
521 let byte = low_byte(byte);
522 let high = match byte.clone() {
523 SymWord::Concrete(value) => SymWord::Concrete(U256::from(value.to::<u8>() >> 4)),
524 byte => SymWord::Expr(Expr::op(ExprOp::Shr, byte.into_expr(), Expr::Const(U256::from(4)))),
525 };
526 let low = match byte {
527 SymWord::Concrete(value) => SymWord::Concrete(U256::from(value.to::<u8>() & 0x0f)),
528 byte => {
529 SymWord::Expr(Expr::op(ExprOp::And, byte.into_expr(), Expr::Const(U256::from(0x0f))))
530 }
531 };
532 out.push(hex_nibble_ascii(high));
533 out.push(hex_nibble_ascii(low));
534}
535
536pub(crate) fn hex_nibble_ascii(nibble: SymWord) -> SymWord {
538 match low_byte(nibble) {
539 SymWord::Concrete(value) => {
540 let nibble = value.to::<u8>() & 0x0f;
541 let byte = if nibble < 10 { b'0' + nibble } else { b'a' + (nibble - 10) };
542 SymWord::Concrete(U256::from(byte))
543 }
544 nibble => {
545 let nibble = nibble.into_expr();
546 SymWord::Expr(Expr::Ite(
547 Box::new(BoolExpr::cmp(
548 BoolExprOp::Ult,
549 nibble.clone(),
550 Expr::Const(U256::from(10)),
551 )),
552 Box::new(Expr::op(ExprOp::Add, nibble.clone(), Expr::Const(U256::from(b'0')))),
553 Box::new(Expr::op(ExprOp::Add, nibble, Expr::Const(U256::from(b'a' - 10)))),
554 ))
555 }
556 }
557}
558
559pub(crate) fn read_abi_word_arg(
561 memory: &SymMemory,
562 args_offset: usize,
563 index: usize,
564) -> Result<SymWord, SymbolicError> {
565 memory.load_word(args_offset + index * 32)
566}
567
568pub(crate) fn read_abi_concrete_word_arg(
570 memory: &SymMemory,
571 args_offset: usize,
572 index: usize,
573 reason: &'static str,
574) -> Result<U256, SymbolicError> {
575 read_abi_word_arg(memory, args_offset, index)?.into_concrete(reason)
576}
577
578pub(crate) fn read_abi_constrained_word_arg(
580 state: &PathState,
581 args_offset: usize,
582 index: usize,
583 reason: &'static str,
584) -> Result<U256, SymbolicError> {
585 let word = read_abi_word_arg(&state.memory, args_offset, index)?;
586 state.expect_constrained_word(word, reason)
587}
588
589pub(crate) fn read_abi_constrained_address_arg(
591 state: &PathState,
592 args_offset: usize,
593 index: usize,
594 reason: &'static str,
595) -> Result<Address, SymbolicError> {
596 Ok(word_to_address(read_abi_constrained_word_arg(state, args_offset, index, reason)?))
597}
598
599pub(crate) fn read_abi_address_or_symbolic_slot_arg(
601 state: &mut PathState,
602 args_offset: usize,
603 index: usize,
604) -> Result<Address, SymbolicError> {
605 let word = read_abi_word_arg(&state.memory, args_offset, index)?;
606 Ok(state.address_or_symbolic_slot(word))
607}
608
609pub(crate) fn read_abi_address_word_or_symbolic_slot_arg(
611 state: &mut PathState,
612 args_offset: usize,
613 index: usize,
614) -> Result<(Address, SymWord), SymbolicError> {
615 let word = read_abi_word_arg(&state.memory, args_offset, index)?;
616 let address = state.address_or_symbolic_slot(word.clone());
617 Ok((address, word))
618}
619
620pub(crate) fn read_abi_address_arg(
622 memory: &SymMemory,
623 args_offset: usize,
624 index: usize,
625 reason: &'static str,
626) -> Result<Address, SymbolicError> {
627 Ok(word_to_address(read_abi_concrete_word_arg(memory, args_offset, index, reason)?))
628}
629
630pub(crate) fn read_abi_bool_arg(
632 memory: &SymMemory,
633 args_offset: usize,
634 index: usize,
635 reason: &'static str,
636) -> Result<bool, SymbolicError> {
637 Ok(!read_abi_concrete_word_arg(memory, args_offset, index, reason)?.is_zero())
638}
639
640pub(crate) fn read_abi_u64_arg(
642 memory: &SymMemory,
643 args_offset: usize,
644 index: usize,
645 reason: &'static str,
646) -> Result<u64, SymbolicError> {
647 let value = read_abi_concrete_word_arg(memory, args_offset, index, reason)?;
648 if value > U256::from(u64::MAX) {
649 return Err(SymbolicError::Unsupported(reason));
650 }
651 Ok(value.to())
652}
653
654pub(crate) fn read_abi_u32_arg(
656 memory: &SymMemory,
657 args_offset: usize,
658 index: usize,
659 reason: &'static str,
660) -> Result<u32, SymbolicError> {
661 let value = read_abi_concrete_word_arg(memory, args_offset, index, reason)?;
662 if value > U256::from(u32::MAX) {
663 return Err(SymbolicError::Unsupported(reason));
664 }
665 Ok(value.to())
666}
667
668pub(crate) fn read_abi_bytes4_words_arg(
670 memory: &SymMemory,
671 args_offset: usize,
672 index: usize,
673) -> Vec<SymWord> {
674 memory.read_bytes(args_offset + index * 32, 4)
675}
676
677pub(crate) fn read_abi_dynamic_bytes_arg(
679 memory: &SymMemory,
680 args_offset: usize,
681 index: usize,
682 reason: &'static str,
683) -> Result<Vec<u8>, SymbolicError> {
684 let offset = read_abi_concrete_word_arg(memory, args_offset, index, reason)?.to::<usize>();
685 let len = memory.load_word(args_offset + offset)?.into_usize(reason)?;
686 memory.read_concrete(args_offset + offset + 32, len)
687}
688
689pub(crate) fn read_abi_symbolic_dynamic_bytes_arg(
691 state: &PathState,
692 args_offset: usize,
693 index: usize,
694 max_len: usize,
695 reason: &'static str,
696) -> Result<Vec<SymWord>, SymbolicError> {
697 let offset = read_abi_word_arg(&state.memory, args_offset, index)?;
698 let offset = state.expect_constrained_usize(offset, reason)?;
699 let len_offset = args_offset.checked_add(offset).ok_or(SymbolicError::Unsupported(reason))?;
700 let len = state.memory.load_word(len_offset)?;
701 let len = state.expect_constrained_usize(len, reason)?;
702 if len > max_len {
703 return Err(SymbolicError::Unsupported(reason));
704 }
705 let data_offset = len_offset.checked_add(32).ok_or(SymbolicError::Unsupported(reason))?;
706 Ok(state.memory.read_bytes(data_offset, len))
707}
708
709pub(crate) fn read_abi_dynamic_return_data_arg(
711 state: &PathState,
712 args_offset: usize,
713 index: usize,
714 max_len: usize,
715 reason: &'static str,
716) -> Result<SymReturnData, SymbolicError> {
717 Ok(SymReturnData::from_symbolic_bytes(read_abi_symbolic_dynamic_bytes_arg(
718 state,
719 args_offset,
720 index,
721 max_len,
722 reason,
723 )?))
724}
725
726pub(crate) fn read_abi_symbolic_dynamic_bytes_array_arg(
728 state: &PathState,
729 args_offset: usize,
730 index: usize,
731 max_array_len: usize,
732 max_bytes_len: usize,
733) -> Result<Vec<SymReturnData>, SymbolicError> {
734 let offset = read_abi_word_arg(&state.memory, args_offset, index)?;
735 let offset = state.expect_constrained_usize(offset, "symbolic vm.mockCalls returns offset")?;
736 let array_offset = args_offset
737 .checked_add(offset)
738 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns offset"))?;
739 let array_data_offset = array_offset
740 .checked_add(32)
741 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns offset"))?;
742 let len = state.memory.load_word(array_offset)?;
743 let len = state.expect_constrained_usize(len, "symbolic vm.mockCalls returns length")?;
744 if len > max_array_len {
745 return Err(SymbolicError::Unsupported("symbolic vm.mockCalls returns length"));
746 }
747
748 let mut values = Vec::with_capacity(len);
749 for value_idx in 0..len {
750 let head_offset = array_offset
751 .checked_add(32)
752 .and_then(|offset| offset.checked_add(value_idx.saturating_mul(32)))
753 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
754 let value_offset = state.memory.load_word(head_offset)?;
755 let value_offset = state.expect_constrained_usize(
756 value_offset,
757 "symbolic vm.mockCalls returns element offset",
758 )?;
759 let len_offset = array_data_offset
760 .checked_add(value_offset)
761 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
762 let value_len = state.memory.load_word(len_offset)?;
763 let value_len = state
764 .expect_constrained_usize(value_len, "symbolic vm.mockCalls returns element length")?;
765 if value_len > max_bytes_len {
766 return Err(SymbolicError::Unsupported("symbolic vm.mockCalls returns element length"));
767 }
768 let data_offset = len_offset
769 .checked_add(32)
770 .ok_or(SymbolicError::Unsupported("symbolic vm.mockCalls returns element offset"))?;
771 values.push(SymReturnData::from_symbolic_bytes(
772 state.memory.read_bytes(data_offset, value_len),
773 ));
774 }
775
776 Ok(values)
777}
778
779pub(crate) fn read_abi_string_arg(
781 memory: &SymMemory,
782 args_offset: usize,
783 index: usize,
784 reason: &'static str,
785) -> Result<String, SymbolicError> {
786 String::from_utf8(read_abi_dynamic_bytes_arg(memory, args_offset, index, reason)?)
787 .map_err(|_| SymbolicError::Unsupported(reason))
788}
789
790pub(crate) fn expected_revert_match_condition(
792 expected: &ExpectedRevert,
793 reverter: Address,
794 return_data: &SymReturnData,
795) -> Option<BoolExpr> {
796 let mut conditions = Vec::new();
797 if let Some(expected_reverter) = &expected.reverter {
798 conditions.push(address_match_condition(expected_reverter, reverter));
799 }
800 match &expected.data {
801 ExpectedRevertData::Any => {}
802 ExpectedRevertData::Prefix(prefix) => {
803 if return_data.len < prefix.len() {
804 return None;
805 }
806 conditions.push(BoolExpr::cmp(
807 BoolExprOp::Uge,
808 return_data.len_expr(),
809 Expr::Const(U256::from(prefix.len())),
810 ));
811 conditions.extend(prefix.iter().enumerate().map(|(offset, expected)| {
812 BoolExpr::eq(return_data.byte(offset).into_expr(), expected.clone().into_expr())
813 }));
814 }
815 ExpectedRevertData::Exact(data) => {
816 if return_data.len < data.len() {
817 return None;
818 }
819 conditions
820 .push(BoolExpr::eq(return_data.len_expr(), Expr::Const(U256::from(data.len()))));
821 conditions.extend(data.iter().enumerate().map(|(offset, expected)| {
822 BoolExpr::eq(return_data.byte(offset).into_expr(), expected.clone().into_expr())
823 }));
824 }
825 }
826 Some(BoolExpr::and(conditions))
827}
828
829pub(crate) fn decode_cheatcode_args(
831 state: &PathState,
832 in_offset: usize,
833 in_size: usize,
834 tys: Vec<DynSolType>,
835) -> Result<Vec<DynSolValue>, SymbolicError> {
836 let data = state.memory.read_concrete(in_offset + 4, in_size.saturating_sub(4))?;
837 let value = DynSolType::Tuple(tys)
838 .abi_decode_sequence(&data)
839 .map_err(|_| SymbolicError::Unsupported("symbolic cheatcode ABI decode"))?;
840 let DynSolValue::Tuple(values) = value else {
841 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
842 };
843 Ok(values)
844}
845
846pub(crate) fn selector_has_string_reason(selector: [u8; 4]) -> bool {
848 selector_in(
849 selector,
850 &[
851 "assertEq(bool[],bool[],string)",
852 "assertEq(uint256[],uint256[],string)",
853 "assertEq(int256[],int256[],string)",
854 "assertEq(address[],address[],string)",
855 "assertEq(bytes32[],bytes32[],string)",
856 "assertEq(string[],string[],string)",
857 "assertEq(bytes[],bytes[],string)",
858 "assertNotEq(bool[],bool[],string)",
859 "assertNotEq(uint256[],uint256[],string)",
860 "assertNotEq(int256[],int256[],string)",
861 "assertNotEq(address[],address[],string)",
862 "assertNotEq(bytes32[],bytes32[],string)",
863 "assertNotEq(string[],string[],string)",
864 "assertNotEq(bytes[],bytes[],string)",
865 "assertEqDecimal(uint256,uint256,uint256,string)",
866 "assertEqDecimal(int256,int256,uint256,string)",
867 ],
868 )
869}
870
871pub(crate) fn array_assertion_element_type(selector: [u8; 4]) -> Result<DynSolType, SymbolicError> {
873 if selector_in(
874 selector,
875 &[
876 "assertEq(bool[],bool[])",
877 "assertEq(bool[],bool[],string)",
878 "assertNotEq(bool[],bool[])",
879 "assertNotEq(bool[],bool[],string)",
880 ],
881 ) {
882 return Ok(DynSolType::Bool);
883 }
884 if selector_in(
885 selector,
886 &[
887 "assertEq(uint256[],uint256[])",
888 "assertEq(uint256[],uint256[],string)",
889 "assertNotEq(uint256[],uint256[])",
890 "assertNotEq(uint256[],uint256[],string)",
891 ],
892 ) {
893 return Ok(DynSolType::Uint(256));
894 }
895 if selector_in(
896 selector,
897 &[
898 "assertEq(int256[],int256[])",
899 "assertEq(int256[],int256[],string)",
900 "assertNotEq(int256[],int256[])",
901 "assertNotEq(int256[],int256[],string)",
902 ],
903 ) {
904 return Ok(DynSolType::Int(256));
905 }
906 if selector_in(
907 selector,
908 &[
909 "assertEq(address[],address[])",
910 "assertEq(address[],address[],string)",
911 "assertNotEq(address[],address[])",
912 "assertNotEq(address[],address[],string)",
913 ],
914 ) {
915 return Ok(DynSolType::Address);
916 }
917 if selector_in(
918 selector,
919 &[
920 "assertEq(bytes32[],bytes32[])",
921 "assertEq(bytes32[],bytes32[],string)",
922 "assertNotEq(bytes32[],bytes32[])",
923 "assertNotEq(bytes32[],bytes32[],string)",
924 ],
925 ) {
926 return Ok(DynSolType::FixedBytes(32));
927 }
928 if selector_in(
929 selector,
930 &[
931 "assertEq(string[],string[])",
932 "assertEq(string[],string[],string)",
933 "assertNotEq(string[],string[])",
934 "assertNotEq(string[],string[],string)",
935 ],
936 ) {
937 return Ok(DynSolType::String);
938 }
939 if selector_in(
940 selector,
941 &[
942 "assertEq(bytes[],bytes[])",
943 "assertEq(bytes[],bytes[],string)",
944 "assertNotEq(bytes[],bytes[])",
945 "assertNotEq(bytes[],bytes[],string)",
946 ],
947 ) {
948 return Ok(DynSolType::Bytes);
949 }
950 Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"))
951}
952
953pub(crate) fn dyn_string(value: &DynSolValue) -> Result<String, SymbolicError> {
955 match value {
956 DynSolValue::String(value) => Ok(value.clone()),
957 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
958 }
959}
960
961pub(crate) fn dyn_bytes(value: &DynSolValue) -> Result<Vec<u8>, SymbolicError> {
963 match value {
964 DynSolValue::Bytes(value) => Ok(value.clone()),
965 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
966 }
967}
968
969pub(crate) const fn dyn_bool(value: &DynSolValue) -> Result<bool, SymbolicError> {
971 match value {
972 DynSolValue::Bool(value) => Ok(*value),
973 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
974 }
975}
976
977pub(crate) const fn dyn_address(value: &DynSolValue) -> Result<Address, SymbolicError> {
979 match value {
980 DynSolValue::Address(value) => Ok(*value),
981 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
982 }
983}
984
985pub(crate) fn dyn_potential_revert(value: &DynSolValue) -> Result<ExpectedRevert, SymbolicError> {
987 let DynSolValue::Tuple(values) = value else {
988 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
989 };
990 let [reverter, partial_match, revert_data] = values.as_slice() else {
991 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
992 };
993
994 let reverter = dyn_address(reverter)?;
995 let reverter = (reverter != Address::ZERO).then(|| SymWord::Concrete(address_word(reverter)));
996 let revert_data = dyn_bytes(revert_data)?
997 .into_iter()
998 .map(|byte| SymWord::Concrete(U256::from(byte)))
999 .collect();
1000 let data = if dyn_bool(partial_match)? {
1001 ExpectedRevertData::Prefix(revert_data)
1002 } else {
1003 ExpectedRevertData::Exact(revert_data)
1004 };
1005 Ok(ExpectedRevert { data, reverter, remaining: 1 })
1006}
1007
1008pub(crate) fn dyn_potential_reverts(
1010 value: &DynSolValue,
1011) -> Result<Vec<ExpectedRevert>, SymbolicError> {
1012 let DynSolValue::Array(values) = value else {
1013 return Err(SymbolicError::Unsupported("symbolic vm.assumeNoRevert decode"));
1014 };
1015 values.iter().map(dyn_potential_revert).collect()
1016}
1017
1018pub(crate) fn dyn_address_array(value: &DynSolValue) -> Result<Vec<Address>, SymbolicError> {
1020 let DynSolValue::Array(values) = value else {
1021 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
1022 };
1023 values.iter().map(dyn_address).collect()
1024}
1025
1026pub(crate) fn dyn_bytes32_array(value: &DynSolValue) -> Result<Vec<B256>, SymbolicError> {
1028 let DynSolValue::Array(values) = value else {
1029 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
1030 };
1031 values
1032 .iter()
1033 .map(|value| match value {
1034 DynSolValue::FixedBytes(bytes, 32) => Ok(*bytes),
1035 _ => Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode")),
1036 })
1037 .collect()
1038}
1039
1040pub(crate) fn dyn_string_array(value: &DynSolValue) -> Result<Vec<String>, SymbolicError> {
1042 let DynSolValue::Array(values) = value else {
1043 return Err(SymbolicError::Unsupported("symbolic cheatcode ABI decode"));
1044 };
1045 values.iter().map(dyn_string).collect()
1046}
1047
1048pub(crate) fn parse_env_array<F>(
1050 value: &str,
1051 delimiter: &str,
1052 mut parser: F,
1053) -> Result<DynSolValue, SymbolicError>
1054where
1055 F: FnMut(&str) -> Result<DynSolValue, SymbolicError>,
1056{
1057 if delimiter.is_empty() {
1058 return Err(SymbolicError::Unsupported("symbolic env delimiter"));
1059 }
1060 value.split(delimiter).map(&mut parser).collect::<Result<Vec<_>, _>>().map(DynSolValue::Array)
1061}
1062
1063pub(crate) fn parse_env_bool_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1065 Ok(DynSolValue::Bool(parse_env_bool(value)?))
1066}
1067
1068pub(crate) fn parse_env_uint_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1070 Ok(DynSolValue::Uint(parse_env_uint(value)?, 256))
1071}
1072
1073pub(crate) fn parse_env_int_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1075 Ok(DynSolValue::Int(I256::from_raw(parse_env_int(value)?), 256))
1076}
1077
1078pub(crate) fn parse_env_address_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1080 Ok(DynSolValue::Address(parse_env_address(value)?))
1081}
1082
1083pub(crate) fn parse_env_bytes32_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1085 Ok(DynSolValue::FixedBytes(B256::from(parse_env_bytes32(value)?.to_be_bytes::<32>()), 32))
1086}
1087
1088pub(crate) fn parse_env_string_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1090 Ok(DynSolValue::String(value.to_string()))
1091}
1092
1093pub(crate) fn parse_env_bytes_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1095 Ok(DynSolValue::Bytes(parse_env_bytes(value)?))
1096}
1097
1098pub(crate) fn parse_env_uint(value: &str) -> Result<U256, SymbolicError> {
1100 value.parse::<U256>().map_err(|_| SymbolicError::Unsupported("symbolic env uint parse"))
1101}
1102
1103pub(crate) fn parse_env_int(value: &str) -> Result<U256, SymbolicError> {
1105 if let Some(value) = value.strip_prefix('-') {
1106 let magnitude = parse_env_uint(value)?;
1107 Ok(U256::ZERO.wrapping_sub(magnitude))
1108 } else {
1109 parse_env_uint(value)
1110 }
1111}
1112
1113pub(crate) fn parse_env_bool(value: &str) -> Result<bool, SymbolicError> {
1115 match value {
1116 "true" | "1" | "TRUE" | "True" => Ok(true),
1117 "false" | "0" | "FALSE" | "False" => Ok(false),
1118 _ => Err(SymbolicError::Unsupported("symbolic env bool parse")),
1119 }
1120}
1121
1122pub(crate) fn parse_env_bytes(value: &str) -> Result<Vec<u8>, SymbolicError> {
1124 let value = value.strip_prefix("0x").unwrap_or(value);
1125 hex::decode(value).map_err(|_| SymbolicError::Unsupported("symbolic env bytes parse"))
1126}
1127
1128pub(crate) fn parse_env_bytes32(value: &str) -> Result<U256, SymbolicError> {
1130 let bytes = parse_env_bytes(value)?;
1131 if bytes.len() != 32 {
1132 return Err(SymbolicError::Unsupported("symbolic env bytes32 parse"));
1133 }
1134 Ok(U256::from_be_slice(&bytes))
1135}
1136
1137pub(crate) fn parse_env_address(value: &str) -> Result<Address, SymbolicError> {
1139 value.parse::<Address>().map_err(|_| SymbolicError::Unsupported("symbolic env address parse"))
1140}
1141
1142pub(crate) fn private_key_signer(private_key: U256) -> Result<PrivateKeySigner, SymbolicError> {
1144 private_key_from_u256(private_key)
1145 .map_err(|_| SymbolicError::Unsupported("symbolic private key parse"))
1146}
1147
1148pub(crate) fn private_key_address(private_key: U256) -> Result<Address, SymbolicError> {
1150 Ok(private_key_signer(private_key)?.address())
1151}
1152
1153pub(crate) fn sign_hash_words(
1155 private_key: U256,
1156 digest: U256,
1157) -> Result<Vec<SymWord>, SymbolicError> {
1158 let signer = private_key_signer(private_key)?;
1159 let digest = B256::from(digest.to_be_bytes::<32>());
1160 let sig = signer
1161 .sign_hash_sync(&digest)
1162 .map_err(|_| SymbolicError::Unsupported("symbolic vm.sign"))?;
1163 Ok(vec![
1164 SymWord::Concrete(U256::from(sig.v() as u64 + 27)),
1165 SymWord::Concrete(sig.r()),
1166 SymWord::Concrete(sig.s()),
1167 ])
1168}
1169
1170pub(crate) fn sign_compact_hash_words(
1172 private_key: U256,
1173 digest: U256,
1174) -> Result<Vec<SymWord>, SymbolicError> {
1175 let signer = private_key_signer(private_key)?;
1176 let digest = B256::from(digest.to_be_bytes::<32>());
1177 let sig = signer
1178 .sign_hash_sync(&digest)
1179 .map_err(|_| SymbolicError::Unsupported("symbolic vm.signCompact"))?;
1180 let y_parity = U256::from(sig.v() as u64) << 255;
1181 Ok(vec![SymWord::Concrete(sig.r()), SymWord::Concrete(sig.s() | y_parity)])
1182}
1183
1184pub(crate) fn derive_private_key<W: Wordlist>(
1186 mnemonic: &str,
1187 path: &str,
1188 index: u32,
1189) -> Result<U256, SymbolicError> {
1190 foundry_common::wallet::derive_private_key::<W>(mnemonic, path, index)
1191 .map_err(|_| SymbolicError::Unsupported("symbolic vm.deriveKey"))
1192}
1193
1194pub(crate) fn derive_private_key_with_language(
1196 mnemonic: &str,
1197 path: &str,
1198 index: u32,
1199 language: &str,
1200) -> Result<U256, SymbolicError> {
1201 foundry_common::wallet::derive_private_key_with_language(mnemonic, path, index, language)
1202 .map_err(|_| SymbolicError::Unsupported("symbolic vm.deriveKey language"))
1203}
1204
1205pub(crate) fn artifact_json_path(path: &str) -> PathBuf {
1207 if path.ends_with(".json") {
1208 return PathBuf::from(path);
1209 }
1210
1211 let mut parts = path.split(':');
1212 let first = parts.next().unwrap_or_default();
1213 let second = parts.next();
1214
1215 if first.contains('.') {
1216 let file = Path::new(first);
1217 let contract = second
1218 .map(str::to_string)
1219 .or_else(|| file.file_stem().map(|stem| stem.to_string_lossy().to_string()))
1220 .unwrap_or_else(|| first.to_string());
1221 PathBuf::from("out").join(first).join(format!("{contract}.json"))
1222 } else {
1223 let contract = first;
1224 PathBuf::from("out").join(format!("{contract}.sol")).join(format!("{contract}.json"))
1225 }
1226}
1227
1228pub(crate) fn artifact_code(path: &str, deployed: bool) -> Result<Vec<u8>, SymbolicError> {
1230 let data = std::fs::read_to_string(artifact_json_path(path))
1231 .map_err(|_| SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1232 let artifact: serde_json::Value = serde_json::from_str(&data)
1233 .map_err(|_| SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1234 let key = if deployed { "deployedBytecode" } else { "bytecode" };
1235 let object = artifact
1236 .get(key)
1237 .and_then(|value| value.get("object").or(Some(value)))
1238 .and_then(serde_json::Value::as_str)
1239 .ok_or(SymbolicError::Unsupported("symbolic vm.getCode artifact"))?;
1240 hex::decode(object).map_err(|_| SymbolicError::Unsupported("symbolic vm.getCode artifact"))
1241}