1use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
2use alloy_primitives::{Address, Bytes, U256};
3use foundry_evm_core::backend::DatabaseExt;
4use revm::{
5 bytecode::Bytecode,
6 context::{ContextTr, JournalTr},
7 interpreter::InstructionResult,
8};
9use std::{cmp::Ordering, collections::VecDeque};
10
11#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
13pub struct MockCallDataContext {
14 pub calldata: Bytes,
16 pub value: Option<U256>,
18}
19
20#[derive(Clone, Debug)]
22pub struct MockCallReturnData {
23 pub ret_type: InstructionResult,
25 pub data: Bytes,
27}
28
29impl PartialOrd for MockCallDataContext {
30 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
31 Some(self.cmp(other))
32 }
33}
34
35impl Ord for MockCallDataContext {
36 fn cmp(&self, other: &Self) -> Ordering {
37 self.calldata.cmp(&other.calldata).reverse().then(self.value.cmp(&other.value).reverse())
43 }
44}
45
46impl Cheatcode for clearMockedCallsCall {
47 fn apply(&self, state: &mut Cheatcodes) -> Result {
48 let Self {} = self;
49 state.mocked_calls = Default::default();
50 Ok(Default::default())
51 }
52}
53
54impl Cheatcode for mockCall_0Call {
55 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
56 &self,
57 ccx: &mut CheatsCtxt<'_, CTX>,
58 ) -> Result {
59 let Self { callee, data, returnData } = self;
60 let _ = make_acc_non_empty(callee, ccx)?;
61
62 mock_call(ccx.state, callee, data, None, returnData, InstructionResult::Return);
63 Ok(Default::default())
64 }
65}
66
67impl Cheatcode for mockCall_1Call {
68 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
69 &self,
70 ccx: &mut CheatsCtxt<'_, CTX>,
71 ) -> Result {
72 let Self { callee, msgValue, data, returnData } = self;
73 let _ = make_acc_non_empty(callee, ccx)?;
74
75 mock_call(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return);
76 Ok(Default::default())
77 }
78}
79
80impl Cheatcode for mockCall_2Call {
81 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
82 &self,
83 ccx: &mut CheatsCtxt<'_, CTX>,
84 ) -> Result {
85 let Self { callee, data, returnData } = self;
86 let _ = make_acc_non_empty(callee, ccx)?;
87
88 mock_call(
89 ccx.state,
90 callee,
91 &Bytes::from(*data),
92 None,
93 returnData,
94 InstructionResult::Return,
95 );
96 Ok(Default::default())
97 }
98}
99
100impl Cheatcode for mockCall_3Call {
101 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
102 &self,
103 ccx: &mut CheatsCtxt<'_, CTX>,
104 ) -> Result {
105 let Self { callee, msgValue, data, returnData } = self;
106 let _ = make_acc_non_empty(callee, ccx)?;
107
108 mock_call(
109 ccx.state,
110 callee,
111 &Bytes::from(*data),
112 Some(msgValue),
113 returnData,
114 InstructionResult::Return,
115 );
116 Ok(Default::default())
117 }
118}
119
120impl Cheatcode for mockCalls_0Call {
121 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
122 &self,
123 ccx: &mut CheatsCtxt<'_, CTX>,
124 ) -> Result {
125 let Self { callee, data, returnData } = self;
126 let _ = make_acc_non_empty(callee, ccx)?;
127
128 mock_calls(ccx.state, callee, data, None, returnData, InstructionResult::Return);
129 Ok(Default::default())
130 }
131}
132
133impl Cheatcode for mockCalls_1Call {
134 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
135 &self,
136 ccx: &mut CheatsCtxt<'_, CTX>,
137 ) -> Result {
138 let Self { callee, msgValue, data, returnData } = self;
139 let _ = make_acc_non_empty(callee, ccx)?;
140
141 mock_calls(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return);
142 Ok(Default::default())
143 }
144}
145
146impl Cheatcode for mockCallRevert_0Call {
147 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
148 &self,
149 ccx: &mut CheatsCtxt<'_, CTX>,
150 ) -> Result {
151 let Self { callee, data, revertData } = self;
152 let _ = make_acc_non_empty(callee, ccx)?;
153
154 mock_call(ccx.state, callee, data, None, revertData, InstructionResult::Revert);
155 Ok(Default::default())
156 }
157}
158
159impl Cheatcode for mockCallRevert_1Call {
160 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
161 &self,
162 ccx: &mut CheatsCtxt<'_, CTX>,
163 ) -> Result {
164 let Self { callee, msgValue, data, revertData } = self;
165 let _ = make_acc_non_empty(callee, ccx)?;
166
167 mock_call(ccx.state, callee, data, Some(msgValue), revertData, InstructionResult::Revert);
168 Ok(Default::default())
169 }
170}
171
172impl Cheatcode for mockCallRevert_2Call {
173 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
174 &self,
175 ccx: &mut CheatsCtxt<'_, CTX>,
176 ) -> Result {
177 let Self { callee, data, revertData } = self;
178 let _ = make_acc_non_empty(callee, ccx)?;
179
180 mock_call(
181 ccx.state,
182 callee,
183 &Bytes::from(*data),
184 None,
185 revertData,
186 InstructionResult::Revert,
187 );
188 Ok(Default::default())
189 }
190}
191
192impl Cheatcode for mockCallRevert_3Call {
193 fn apply_stateful<CTX: ContextTr<Db: DatabaseExt>>(
194 &self,
195 ccx: &mut CheatsCtxt<'_, CTX>,
196 ) -> Result {
197 let Self { callee, msgValue, data, revertData } = self;
198 let _ = make_acc_non_empty(callee, ccx)?;
199
200 mock_call(
201 ccx.state,
202 callee,
203 &Bytes::from(*data),
204 Some(msgValue),
205 revertData,
206 InstructionResult::Revert,
207 );
208 Ok(Default::default())
209 }
210}
211
212impl Cheatcode for mockFunctionCall {
213 fn apply(&self, state: &mut Cheatcodes) -> Result {
214 let Self { callee, target, data } = self;
215 state.mocked_functions.entry(*callee).or_default().insert(data.clone(), *target);
216
217 Ok(Default::default())
218 }
219}
220
221fn mock_call(
222 state: &mut Cheatcodes,
223 callee: &Address,
224 cdata: &Bytes,
225 value: Option<&U256>,
226 rdata: &Bytes,
227 ret_type: InstructionResult,
228) {
229 mock_calls(state, callee, cdata, value, std::slice::from_ref(rdata), ret_type)
230}
231
232fn mock_calls(
233 state: &mut Cheatcodes,
234 callee: &Address,
235 cdata: &Bytes,
236 value: Option<&U256>,
237 rdata_vec: &[Bytes],
238 ret_type: InstructionResult,
239) {
240 state.mocked_calls.entry(*callee).or_default().insert(
241 MockCallDataContext { calldata: cdata.clone(), value: value.copied() },
242 rdata_vec
243 .iter()
244 .map(|rdata| MockCallReturnData { ret_type, data: rdata.clone() })
245 .collect::<VecDeque<_>>(),
246 );
247}
248
249fn make_acc_non_empty<CTX: ContextTr<Db: DatabaseExt>>(
252 callee: &Address,
253 ccx: &mut CheatsCtxt<'_, CTX>,
254) -> Result {
255 let empty_bytecode = {
256 let acc = ccx.ecx.journal_mut().load_account(*callee)?;
257 acc.info.code.as_ref().is_none_or(Bytecode::is_empty)
258 };
259 if empty_bytecode {
260 let code = Bytecode::new_raw(Bytes::from_static(&[0u8]));
261 ccx.ecx.journal_mut().set_code(*callee, code);
262 }
263
264 Ok(Default::default())
265}