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::{hex, U256};
6use alloy_sol_types::SolValue;
7
8// address
9impl Cheatcode for toString_0Call {
10    fn apply(&self, _state: &mut Cheatcodes) -> Result {
11        let Self { value } = self;
12        Ok(value.to_string().abi_encode())
13    }
14}
15
16// bytes
17impl Cheatcode for toString_1Call {
18    fn apply(&self, _state: &mut Cheatcodes) -> Result {
19        let Self { value } = self;
20        Ok(value.to_string().abi_encode())
21    }
22}
23
24// bytes32
25impl Cheatcode for toString_2Call {
26    fn apply(&self, _state: &mut Cheatcodes) -> Result {
27        let Self { value } = self;
28        Ok(value.to_string().abi_encode())
29    }
30}
31
32// bool
33impl Cheatcode for toString_3Call {
34    fn apply(&self, _state: &mut Cheatcodes) -> Result {
35        let Self { value } = self;
36        Ok(value.to_string().abi_encode())
37    }
38}
39
40// uint256
41impl Cheatcode for toString_4Call {
42    fn apply(&self, _state: &mut Cheatcodes) -> Result {
43        let Self { value } = self;
44        Ok(value.to_string().abi_encode())
45    }
46}
47
48// int256
49impl Cheatcode for toString_5Call {
50    fn apply(&self, _state: &mut Cheatcodes) -> Result {
51        let Self { value } = self;
52        Ok(value.to_string().abi_encode())
53    }
54}
55
56impl Cheatcode for parseBytesCall {
57    fn apply(&self, _state: &mut Cheatcodes) -> Result {
58        let Self { stringifiedValue } = self;
59        parse(stringifiedValue, &DynSolType::Bytes)
60    }
61}
62
63impl Cheatcode for parseAddressCall {
64    fn apply(&self, _state: &mut Cheatcodes) -> Result {
65        let Self { stringifiedValue } = self;
66        parse(stringifiedValue, &DynSolType::Address)
67    }
68}
69
70impl Cheatcode for parseUintCall {
71    fn apply(&self, _state: &mut Cheatcodes) -> Result {
72        let Self { stringifiedValue } = self;
73        parse(stringifiedValue, &DynSolType::Uint(256))
74    }
75}
76
77impl Cheatcode for parseIntCall {
78    fn apply(&self, _state: &mut Cheatcodes) -> Result {
79        let Self { stringifiedValue } = self;
80        parse(stringifiedValue, &DynSolType::Int(256))
81    }
82}
83
84impl Cheatcode for parseBytes32Call {
85    fn apply(&self, _state: &mut Cheatcodes) -> Result {
86        let Self { stringifiedValue } = self;
87        parse(stringifiedValue, &DynSolType::FixedBytes(32))
88    }
89}
90
91impl Cheatcode for parseBoolCall {
92    fn apply(&self, _state: &mut Cheatcodes) -> Result {
93        let Self { stringifiedValue } = self;
94        parse(stringifiedValue, &DynSolType::Bool)
95    }
96}
97
98impl Cheatcode for toLowercaseCall {
99    fn apply(&self, _state: &mut Cheatcodes) -> Result {
100        let Self { input } = self;
101        Ok(input.to_lowercase().abi_encode())
102    }
103}
104
105impl Cheatcode for toUppercaseCall {
106    fn apply(&self, _state: &mut Cheatcodes) -> Result {
107        let Self { input } = self;
108        Ok(input.to_uppercase().abi_encode())
109    }
110}
111
112impl Cheatcode for trimCall {
113    fn apply(&self, _state: &mut Cheatcodes) -> Result {
114        let Self { input } = self;
115        Ok(input.trim().abi_encode())
116    }
117}
118
119impl Cheatcode for replaceCall {
120    fn apply(&self, _state: &mut Cheatcodes) -> Result {
121        let Self { input, from, to } = self;
122        Ok(input.replace(from, to).abi_encode())
123    }
124}
125
126impl Cheatcode for splitCall {
127    fn apply(&self, _state: &mut Cheatcodes) -> Result {
128        let Self { input, delimiter } = self;
129        let parts: Vec<&str> = input.split(delimiter).collect();
130        Ok(parts.abi_encode())
131    }
132}
133
134impl Cheatcode for indexOfCall {
135    fn apply(&self, _state: &mut Cheatcodes) -> Result {
136        let Self { input, key } = self;
137        Ok(input.find(key).map(U256::from).unwrap_or(U256::MAX).abi_encode())
138    }
139}
140
141impl Cheatcode for containsCall {
142    fn apply(&self, _state: &mut Cheatcodes) -> Result {
143        let Self { subject, search } = self;
144        Ok(subject.contains(search).abi_encode())
145    }
146}
147
148pub(super) fn parse(s: &str, ty: &DynSolType) -> Result {
149    parse_value(s, ty).map(|v| v.abi_encode())
150}
151
152pub(super) fn parse_array<I, S>(values: I, ty: &DynSolType) -> Result
153where
154    I: IntoIterator<Item = S>,
155    S: AsRef<str>,
156{
157    let mut values = values.into_iter();
158    match values.next() {
159        Some(first) if !first.as_ref().is_empty() => std::iter::once(first)
160            .chain(values)
161            .map(|s| parse_value(s.as_ref(), ty))
162            .collect::<Result<Vec<_>, _>>()
163            .map(|vec| DynSolValue::Array(vec).abi_encode()),
164        // return the empty encoded Bytes when values is empty or the first element is empty
165        _ => Ok("".abi_encode()),
166    }
167}
168
169#[instrument(target = "cheatcodes", level = "debug", skip(ty), fields(%ty), ret)]
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                return Some(Err("missing hex prefix (\"0x\") for hex string"));
200            }
201        }
202        _ => {}
203    }
204    None
205}