Skip to main content

foundry_cheatcodes/
test.rs

1//! Implementations of [`Testing`](spec::Group::Testing) cheatcodes.
2
3use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
4use alloy_chains::Chain as AlloyChain;
5use alloy_primitives::{Address, Bytes, U256};
6use alloy_sol_types::SolValue;
7use foundry_common::version::SEMVER_VERSION;
8use foundry_evm_core::{constants::MAGIC_SKIP, evm::FoundryEvmNetwork};
9use revm::context::{ContextTr, JournalTr};
10use std::str::FromStr;
11
12pub(crate) mod assert;
13pub(crate) mod assume;
14pub(crate) mod expect;
15pub(crate) mod revert_handlers;
16
17impl Cheatcode for breakpoint_0Call {
18    fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
19        let Self { char } = self;
20        breakpoint(ccx.state, &ccx.caller, char, true)
21    }
22}
23
24impl Cheatcode for breakpoint_1Call {
25    fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
26        let Self { char, value } = self;
27        breakpoint(ccx.state, &ccx.caller, char, *value)
28    }
29}
30
31impl Cheatcode for getFoundryVersionCall {
32    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
33        let Self {} = self;
34        Ok(SEMVER_VERSION.abi_encode())
35    }
36}
37
38impl Cheatcode for rpcUrlCall {
39    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
40        let Self { rpcAlias } = self;
41        let url = state.config.rpc_endpoint(rpcAlias)?.url()?.abi_encode();
42        Ok(url)
43    }
44}
45
46impl Cheatcode for rpcUrlsCall {
47    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
48        let Self {} = self;
49        state.config.rpc_urls().map(|urls| urls.abi_encode())
50    }
51}
52
53impl Cheatcode for rpcUrlStructsCall {
54    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
55        let Self {} = self;
56        state.config.rpc_urls().map(|urls| urls.abi_encode())
57    }
58}
59
60impl Cheatcode for sleepCall {
61    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
62        let Self { duration } = self;
63        let sleep_duration = std::time::Duration::from_millis(duration.saturating_to());
64        std::thread::sleep(sleep_duration);
65        Ok(Default::default())
66    }
67}
68
69impl Cheatcode for skip_0Call {
70    fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
71        let Self { skipTest } = *self;
72        skip(ccx, skipTest, "")
73    }
74}
75
76impl Cheatcode for skip_1Call {
77    fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
78        let Self { skipTest, reason } = self;
79        skip(ccx, *skipTest, reason)
80    }
81}
82
83impl Cheatcode for getChain_0Call {
84    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
85        let Self { chainAlias } = self;
86        get_chain(state, chainAlias)
87    }
88}
89
90impl Cheatcode for getChain_1Call {
91    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
92        let Self { chainId } = self;
93        // Convert the chainId to a string and use the existing get_chain function
94        let chain_id_str = chainId.to_string();
95        get_chain(state, &chain_id_str)
96    }
97}
98
99/// Reverts with the magic skip payload and records it in the state, so that the executor can
100/// distinguish this genuine skip from user-crafted revert data carrying the same prefix.
101fn skip<FEN: FoundryEvmNetwork>(
102    ccx: &mut CheatsCtxt<'_, '_, FEN>,
103    skip_test: bool,
104    reason: &str,
105) -> Result {
106    if !skip_test {
107        return Ok(Default::default());
108    }
109    // Skip should not work if called deeper than at test level.
110    // Since we're not returning the magic skip bytes, this will cause a test failure.
111    ensure!(ccx.ecx.journal().depth() <= 1, "`skip` can only be used at test level");
112    let payload = Bytes::from([MAGIC_SKIP, reason.as_bytes()].concat());
113    ccx.state.skip_payloads.push(payload.clone());
114    Err(payload.into())
115}
116
117/// Adds or removes the given breakpoint to the state.
118fn breakpoint<FEN: FoundryEvmNetwork>(
119    state: &mut Cheatcodes<FEN>,
120    caller: &Address,
121    s: &str,
122    add: bool,
123) -> Result {
124    let mut chars = s.chars();
125    let (Some(point), None) = (chars.next(), chars.next()) else {
126        bail!("breakpoints must be exactly one character");
127    };
128    ensure!(point.is_alphabetic(), "only alphabetic characters are accepted as breakpoints");
129
130    if add {
131        state.breakpoints.insert(point, (*caller, state.pc));
132    } else {
133        state.breakpoints.remove(&point);
134    }
135
136    Ok(Default::default())
137}
138
139/// Gets chain information for the given alias.
140fn get_chain<FEN: FoundryEvmNetwork>(state: &mut Cheatcodes<FEN>, chain_alias: &str) -> Result {
141    // Parse the chain alias - works for both chain names and IDs
142    let alloy_chain = AlloyChain::from_str(chain_alias)
143        .map_err(|_| fmt_err!("invalid chain alias: {chain_alias}"))?;
144    let chain_name = alloy_chain.to_string();
145    let chain_id = alloy_chain.id();
146
147    // Check if this is an unknown chain ID by comparing the name to the chain ID
148    // When a numeric ID is passed for an unknown chain, alloy_chain.to_string() will return the ID
149    // So if they match, it's likely an unknown chain ID
150    if chain_name == chain_id.to_string() {
151        return Err(fmt_err!("invalid chain alias: {chain_alias}"));
152    }
153
154    // Try to retrieve RPC URL and chain alias from user's config in foundry.toml.
155    let (rpc_url, chain_alias) = if let Some(rpc_url) =
156        state.config.rpc_endpoint(&chain_name).ok().and_then(|e| e.url().ok())
157    {
158        (rpc_url, chain_name.clone())
159    } else {
160        (String::new(), chain_alias.to_string())
161    };
162
163    let chain_struct = Chain {
164        name: chain_name,
165        chainId: U256::from(chain_id),
166        chainAlias: chain_alias,
167        rpcUrl: rpc_url,
168    };
169
170    Ok(chain_struct.abi_encode())
171}