Skip to main content

foundry_config/
invariant.rs

1//! Configuration for invariant testing
2
3use crate::fuzz::FuzzDictionaryConfig;
4use alloy_primitives::U256;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8/// Contains for invariant testing
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10pub struct InvariantConfig {
11    /// The number of runs that must execute for each invariant test group.
12    pub runs: u32,
13    /// The number of calls executed to attempt to break invariants in one run.
14    pub depth: u32,
15    /// Fails the invariant fuzzing if a revert occurs
16    pub fail_on_revert: bool,
17    /// Allows overriding an unsafe external call when running invariant tests. eg. reentrancy
18    /// checks
19    pub call_override: bool,
20    /// The fuzz dictionary configuration
21    #[serde(flatten)]
22    pub dictionary: FuzzDictionaryConfig,
23    /// The maximum number of attempts to shrink the sequence
24    pub shrink_run_limit: u32,
25    /// The maximum number of rejects via `vm.assume` which can be encountered during a single
26    /// invariant run.
27    pub max_assume_rejects: u32,
28    /// Number of runs to execute and include in the gas report.
29    pub gas_report_samples: u32,
30    /// Path where invariant corpus is stored, enables coverage guided fuzzing and edge coverage
31    /// metrics.
32    pub corpus_dir: Option<PathBuf>,
33    /// Whether corpus to use gzip file compression and decompression.
34    pub corpus_gzip: bool,
35    // Number of corpus mutations until marked as eligible to be flushed from memory.
36    pub corpus_min_mutations: usize,
37    // Number of corpus that won't be evicted from memory.
38    pub corpus_min_size: usize,
39    /// Path where invariant failures are recorded and replayed.
40    pub failure_persist_dir: Option<PathBuf>,
41    /// Whether to collect and display fuzzed selectors metrics.
42    pub show_metrics: bool,
43    /// Optional timeout (in seconds) for each invariant test.
44    pub timeout: Option<u32>,
45    /// Display counterexample as solidity calls.
46    pub show_solidity: bool,
47    /// Whether to collect and display edge coverage metrics.
48    pub show_edge_coverage: bool,
49    /// Maximum value for fuzzed integers, used to simulate smaller integer types.
50    /// When set, unsigned integers are clamped to [0, max_fuzz_int] and signed integers
51    /// are clamped to [-(max_fuzz_int+1), max_fuzz_int] to match real signed type ranges.
52    /// Used for Polkadot compatibility where balances are u128.
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub max_fuzz_int: Option<U256>,
55}
56
57impl Default for InvariantConfig {
58    fn default() -> Self {
59        Self {
60            runs: 256,
61            depth: 500,
62            fail_on_revert: false,
63            call_override: false,
64            dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() },
65            shrink_run_limit: 5000,
66            max_assume_rejects: 65536,
67            gas_report_samples: 256,
68            corpus_dir: None,
69            corpus_gzip: true,
70            corpus_min_mutations: 5,
71            corpus_min_size: 0,
72            failure_persist_dir: None,
73            show_metrics: true,
74            timeout: None,
75            show_solidity: false,
76            show_edge_coverage: false,
77            max_fuzz_int: None,
78        }
79    }
80}
81
82impl InvariantConfig {
83    /// Creates invariant configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir.
84    pub fn new(cache_dir: PathBuf) -> Self {
85        Self {
86            runs: 256,
87            depth: 500,
88            fail_on_revert: false,
89            call_override: false,
90            dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() },
91            shrink_run_limit: 5000,
92            max_assume_rejects: 65536,
93            gas_report_samples: 256,
94            corpus_dir: None,
95            corpus_gzip: true,
96            corpus_min_mutations: 5,
97            corpus_min_size: 0,
98            failure_persist_dir: Some(cache_dir),
99            show_metrics: true,
100            timeout: None,
101            show_solidity: false,
102            show_edge_coverage: false,
103            max_fuzz_int: None,
104        }
105    }
106}