Skip to main content

foundry_cheatcodes/
string.rs

1//! Implementations of [`String`](spec::Group::String) cheatcodes.
2
3use crate::{Cheatcode, Cheatcodes, Result, Vm::*};
4use alloy_dyn_abi::{DynSolType, DynSolValue};
5use alloy_primitives::{U256, hex};
6use alloy_sol_types::SolValue;
7use foundry_evm_core::evm::FoundryEvmNetwork;
8
9// address
10impl Cheatcode for toString_0Call {
11    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
12        let Self { value } = self;
13        Ok(value.to_string().abi_encode())
14    }
15}
16
17// bytes
18impl Cheatcode for toString_1Call {
19    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
20        let Self { value } = self;
21        Ok(value.to_string().abi_encode())
22    }
23}
24
25// bytes32
26impl Cheatcode for toString_2Call {
27    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
28        let Self { value } = self;
29        Ok(value.to_string().abi_encode())
30    }
31}
32
33// bool
34impl Cheatcode for toString_3Call {
35    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
36        let Self { value } = self;
37        Ok(value.to_string().abi_encode())
38    }
39}
40
41// uint256
42impl Cheatcode for toString_4Call {
43    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
44        let Self { value } = self;
45        Ok(value.to_string().abi_encode())
46    }
47}
48
49// int256
50impl Cheatcode for toString_5Call {
51    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
52        let Self { value } = self;
53        Ok(value.to_string().abi_encode())
54    }
55}
56
57impl Cheatcode for parseBytesCall {
58    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
59        let Self { stringifiedValue } = self;
60        parse(stringifiedValue, &DynSolType::Bytes)
61    }
62}
63
64impl Cheatcode for parseAddressCall {
65    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
66        let Self { stringifiedValue } = self;
67        parse(stringifiedValue, &DynSolType::Address)
68    }
69}
70
71impl Cheatcode for parseUintCall {
72    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
73        let Self { stringifiedValue } = self;
74        parse(stringifiedValue, &DynSolType::Uint(256))
75    }
76}
77
78impl Cheatcode for parseIntCall {
79    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
80        let Self { stringifiedValue } = self;
81        parse(stringifiedValue, &DynSolType::Int(256))
82    }
83}
84
85impl Cheatcode for parseBytes32Call {
86    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
87        let Self { stringifiedValue } = self;
88        parse(stringifiedValue, &DynSolType::FixedBytes(32))
89    }
90}
91
92impl Cheatcode for parseBoolCall {
93    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
94        let Self { stringifiedValue } = self;
95        parse(stringifiedValue, &DynSolType::Bool)
96    }
97}
98
99impl Cheatcode for toLowercaseCall {
100    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
101        let Self { input } = self;
102        Ok(input.to_lowercase().abi_encode())
103    }
104}
105
106impl Cheatcode for toUppercaseCall {
107    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
108        let Self { input } = self;
109        Ok(input.to_uppercase().abi_encode())
110    }
111}
112
113impl Cheatcode for trimCall {
114    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
115        let Self { input } = self;
116        Ok(input.trim().abi_encode())
117    }
118}
119
120impl Cheatcode for replaceCall {
121    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
122        let Self { input, from, to } = self;
123        Ok(input.replace(from, to).abi_encode())
124    }
125}
126
127impl Cheatcode for splitCall {
128    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
129        let Self { input, delimiter } = self;
130        let parts: Vec<&str> = input.split(delimiter).collect();
131        Ok(parts.abi_encode())
132    }
133}
134
135impl Cheatcode for indexOfCall {
136    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
137        let Self { input, key } = self;
138        Ok(input.find(key).map(U256::from).unwrap_or(U256::MAX).abi_encode())
139    }
140}
141
142impl Cheatcode for containsCall {
143    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
144        let Self { subject, search } = self;
145        Ok(subject.contains(search).abi_encode())
146    }
147}
148
149pub(super) fn parse(s: &str, ty: &DynSolType) -> Result {
150    parse_value(s, ty).map(|v| v.abi_encode())
151}
152
153pub(super) fn parse_array<I, S>(values: I, ty: &DynSolType) -> Result
154where
155    I: IntoIterator<Item = S>,
156    S: AsRef<str>,
157{
158    let mut values = values.into_iter();
159    match values.next() {
160        Some(first) if !first.as_ref().is_empty() => std::iter::once(first)
161            .chain(values)
162            .map(|s| parse_value(s.as_ref(), ty))
163            .collect::<Result<Vec<_>, _>>()
164            .map(|vec| DynSolValue::Array(vec).abi_encode()),
165        // return the empty encoded Bytes when values is empty or the first element is empty
166        _ => Ok("".abi_encode()),
167    }
168}
169
170pub(super) fn parse_value(s: &str, ty: &DynSolType) -> Result<DynSolValue> {
171    match ty.coerce_str(s) {
172        Ok(value) => Ok(value),
173        Err(e) => match parse_value_fallback(s, ty) {
174            Some(Ok(value)) => Ok(value),
175            Some(Err(e2)) => Err(fmt_err!("failed parsing {s:?} as type `{ty}`: {e2}")),
176            None => Err(fmt_err!("failed parsing {s:?} as type `{ty}`: {e}")),
177        },
178    }
179}
180
181// More lenient parsers than `coerce_str`.
182fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option<Result<DynSolValue, &'static str>> {
183    match ty {
184        DynSolType::Bool => {
185            let b = match s {
186                "1" => true,
187                "0" => false,
188                s if s.eq_ignore_ascii_case("true") => true,
189                s if s.eq_ignore_ascii_case("false") => false,
190                _ => return None,
191            };
192            return Some(Ok(DynSolValue::Bool(b)));
193        }
194        DynSolType::Int(_)
195        | DynSolType::Uint(_)
196        | DynSolType::FixedBytes(_)
197        | DynSolType::Bytes
198            if !s.starts_with("0x") && hex::check_raw(s) =>
199        {
200            return Some(Err("missing hex prefix (\"0x\") for hex string"));
201        }
202        _ => {}
203    }
204    None
205}