Skip to main content

foundry_evm_fuzz/strategies/
invariants.rs

1use super::TxGenerator;
2use crate::{CallDetails, FuzzFixtures, strategies::EvmFuzzState};
3use alloy_json_abi::Function;
4use alloy_primitives::Address;
5use parking_lot::RwLock;
6use proptest::prelude::*;
7use rand::seq::IteratorRandom;
8use std::sync::Arc;
9
10/// Given a target address, we generate random calldata.
11pub fn override_call_strat(
12    fuzz_state: EvmFuzzState,
13    contracts: Vec<(Address, Vec<Function>)>,
14    target: Arc<RwLock<Address>>,
15    fuzz_fixtures: FuzzFixtures,
16    dictionary_weight: u32,
17    payable_value_weight: u32,
18) -> impl Strategy<Value = CallDetails> + Send + Sync + 'static {
19    let contracts = Arc::new(contracts);
20    let contracts_ref = contracts.clone();
21    proptest::prop_oneof![
22        80 => proptest::strategy::LazyJust::new(move || *target.read()),
23        20 => any::<prop::sample::Selector>()
24            .prop_map(move |selector| {
25                let (target, _) = selector.select(contracts_ref.iter());
26                *target
27            }),
28    ]
29    .prop_flat_map(move |target_address| {
30        let fuzz_state = fuzz_state.clone();
31        let fuzz_fixtures = fuzz_fixtures.clone();
32        let contracts = contracts.clone();
33
34        let (actual_target, func) = {
35            // If the target address is in the contracts map, use it directly.
36            // Otherwise, fall back to a random contract from the targeted contracts.
37            // This can happen when call_override sets target_reference to a contract
38            // that is not in targetContracts (e.g., the protocol contract during reentrancy).
39            let (actual_target, fuzzed_functions) = contracts
40                .iter()
41                .find(|(address, _)| *address == target_address)
42                .map(|(address, functions)| (*address, functions.clone()))
43                .unwrap_or_else(|| {
44                    let (address, functions) = contracts
45                        .iter()
46                        .choose(&mut rand::rng())
47                        .expect("at least one target contract");
48                    (*address, functions.clone())
49                });
50            (
51                actual_target,
52                any::<prop::sample::Index>()
53                    .prop_map(move |index| index.get(&fuzzed_functions).clone()),
54            )
55        };
56
57        func.prop_flat_map(move |func| {
58            TxGenerator::call_strategy(
59                &fuzz_state,
60                &fuzz_fixtures,
61                actual_target,
62                func,
63                dictionary_weight,
64                payable_value_weight,
65            )
66        })
67    })
68}