Skip to main content

foundry_evm_symbolic/runtime/
cheatcodes.rs

1use foundry_common::wallet::private_key_from_u256;
2
3use super::*;
4
5/// Implements the `foundry_cheatcode_min_input_size` cheatcode runtime helper.
6pub(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
312/// Returns the `symbolic_vm_cheatcode_min_input_size` cheatcode runtime helper result.
313pub(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
361/// Returns the `abi_static_input_size` cheatcode runtime helper result.
362pub(crate) const fn abi_static_input_size(words: usize) -> usize {
363    4 + words * 32
364}
365
366/// Returns the `selector_in` cheatcode runtime helper result.
367pub(crate) fn selector_in(selector: [u8; 4], signatures: &[&str]) -> bool {
368    signatures.iter().any(|signature| selector == selector_for(signature))
369}
370
371/// Returns the `abi_bytes_return` cheatcode runtime helper result.
372pub(crate) fn abi_bytes_return(bytes: Vec<SymWord>) -> SymReturnData {
373    abi_bytes_return_with_len(SymWord::Concrete(U256::from(bytes.len())), bytes)
374}
375
376/// Returns the `abi_bytes_return_with_len` cheatcode runtime helper result.
377pub(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
385/// Returns the `abi_concrete_bytes_return` cheatcode runtime helper result.
386pub(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
390/// Returns the `abi_concrete_value_return` cheatcode runtime helper result.
391pub(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
397/// Implements the `recorded_logs_return_data` cheatcode runtime helper.
398pub(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
426/// Implements the `recorded_logs_json_return_data` cheatcode runtime helper.
427pub(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
472/// Implements the `accesses_return_data` cheatcode runtime helper.
473pub(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
483/// Implements the `complete_cheatcode_call` cheatcode runtime helper.
484pub(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
497/// Implements the `storage_slots_abi_array` cheatcode runtime helper.
498pub(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
507/// Applies the `push_ascii` cheatcode runtime helper.
508pub(crate) fn push_ascii(out: &mut Vec<SymWord>, value: &str) {
509    out.extend(value.bytes().map(|byte| SymWord::Concrete(U256::from(byte))));
510}
511
512/// Applies the `push_hex_word` cheatcode runtime helper.
513pub(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
519/// Applies the `push_hex_byte` cheatcode runtime helper.
520pub(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
536/// Implements the `hex_nibble_ascii` cheatcode runtime helper.
537pub(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
559/// Returns the `read_abi_word_arg` cheatcode runtime helper result.
560pub(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
568/// Returns the `read_abi_concrete_word_arg` cheatcode runtime helper result.
569pub(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
578/// Returns the `read_abi_constrained_word_arg` cheatcode runtime helper result.
579pub(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
589/// Returns the `read_abi_constrained_address_arg` cheatcode runtime helper result.
590pub(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
599/// Returns the `read_abi_address_or_symbolic_slot_arg` cheatcode runtime helper result.
600pub(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
609/// Returns the `read_abi_address_word_or_symbolic_slot_arg` cheatcode runtime helper result.
610pub(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
620/// Returns the `read_abi_address_arg` cheatcode runtime helper result.
621pub(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
630/// Returns the `read_abi_bool_arg` cheatcode runtime helper result.
631pub(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
640/// Returns the `read_abi_u64_arg` cheatcode runtime helper result.
641pub(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
654/// Returns the `read_abi_u32_arg` cheatcode runtime helper result.
655pub(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
668/// Returns the `read_abi_bytes4_words_arg` cheatcode runtime helper result.
669pub(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
677/// Returns the `read_abi_dynamic_bytes_arg` cheatcode runtime helper result.
678pub(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
689/// Returns the `read_abi_symbolic_dynamic_bytes_arg` cheatcode runtime helper result.
690pub(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
709/// Returns the `read_abi_dynamic_return_data_arg` cheatcode runtime helper result.
710pub(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
726/// Returns the `read_abi_symbolic_dynamic_bytes_array_arg` cheatcode runtime helper result.
727pub(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
779/// Returns the `read_abi_string_arg` cheatcode runtime helper result.
780pub(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
790/// Implements the `expected_revert_match_condition` cheatcode runtime helper.
791pub(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
829/// Returns the `decode_cheatcode_args` cheatcode runtime helper result.
830pub(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
846/// Returns the `selector_has_string_reason` cheatcode runtime helper result.
847pub(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
871/// Implements the `array_assertion_element_type` cheatcode runtime helper.
872pub(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
953/// Returns the `dyn_string` cheatcode runtime helper result.
954pub(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
961/// Returns the `dyn_bytes` cheatcode runtime helper result.
962pub(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
969/// Returns the `dyn_bool` cheatcode runtime helper result.
970pub(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
977/// Returns the `dyn_address` cheatcode runtime helper result.
978pub(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
985/// Returns the `dyn_potential_revert` cheatcode runtime helper result.
986pub(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
1008/// Returns the `dyn_potential_reverts` cheatcode runtime helper result.
1009pub(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
1018/// Returns the `dyn_address_array` cheatcode runtime helper result.
1019pub(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
1026/// Returns the `dyn_bytes32_array` cheatcode runtime helper result.
1027pub(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
1040/// Returns the `dyn_string_array` cheatcode runtime helper result.
1041pub(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
1048/// Returns the `parse_env_array` cheatcode runtime helper result.
1049pub(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
1063/// Returns the `parse_env_bool_value` cheatcode runtime helper result.
1064pub(crate) fn parse_env_bool_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1065    Ok(DynSolValue::Bool(parse_env_bool(value)?))
1066}
1067
1068/// Returns the `parse_env_uint_value` cheatcode runtime helper result.
1069pub(crate) fn parse_env_uint_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1070    Ok(DynSolValue::Uint(parse_env_uint(value)?, 256))
1071}
1072
1073/// Returns the `parse_env_int_value` cheatcode runtime helper result.
1074pub(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
1078/// Returns the `parse_env_address_value` cheatcode runtime helper result.
1079pub(crate) fn parse_env_address_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1080    Ok(DynSolValue::Address(parse_env_address(value)?))
1081}
1082
1083/// Returns the `parse_env_bytes32_value` cheatcode runtime helper result.
1084pub(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
1088/// Returns the `parse_env_string_value` cheatcode runtime helper result.
1089pub(crate) fn parse_env_string_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1090    Ok(DynSolValue::String(value.to_string()))
1091}
1092
1093/// Returns the `parse_env_bytes_value` cheatcode runtime helper result.
1094pub(crate) fn parse_env_bytes_value(value: &str) -> Result<DynSolValue, SymbolicError> {
1095    Ok(DynSolValue::Bytes(parse_env_bytes(value)?))
1096}
1097
1098/// Returns the `parse_env_uint` cheatcode runtime helper result.
1099pub(crate) fn parse_env_uint(value: &str) -> Result<U256, SymbolicError> {
1100    value.parse::<U256>().map_err(|_| SymbolicError::Unsupported("symbolic env uint parse"))
1101}
1102
1103/// Returns the `parse_env_int` cheatcode runtime helper result.
1104pub(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
1113/// Returns the `parse_env_bool` cheatcode runtime helper result.
1114pub(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
1122/// Returns the `parse_env_bytes` cheatcode runtime helper result.
1123pub(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
1128/// Returns the `parse_env_bytes32` cheatcode runtime helper result.
1129pub(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
1137/// Returns the `parse_env_address` cheatcode runtime helper result.
1138pub(crate) fn parse_env_address(value: &str) -> Result<Address, SymbolicError> {
1139    value.parse::<Address>().map_err(|_| SymbolicError::Unsupported("symbolic env address parse"))
1140}
1141
1142/// Implements the `private_key_signer` cheatcode runtime helper.
1143pub(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
1148/// Implements the `private_key_address` cheatcode runtime helper.
1149pub(crate) fn private_key_address(private_key: U256) -> Result<Address, SymbolicError> {
1150    Ok(private_key_signer(private_key)?.address())
1151}
1152
1153/// Implements the `sign_hash_words` cheatcode runtime helper.
1154pub(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
1170/// Implements the `sign_compact_hash_words` cheatcode runtime helper.
1171pub(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
1184/// Computes the `derive_private_key` cheatcode runtime helper result.
1185pub(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
1194/// Computes the `derive_private_key_with_language` cheatcode runtime helper result.
1195pub(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
1205/// Implements the `artifact_json_path` cheatcode runtime helper.
1206pub(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
1228/// Implements the `artifact_code` cheatcode runtime helper.
1229pub(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}