1use alloy::primitives::{Address, Bytes, U256};
4use alloy::sol_types::SolCall;
5use std::future::Future;
6
7use super::Operation;
8use crate::error::Result;
9use crate::simulation::SimulationResult;
10
11pub trait CallBuilder: Sized {
16 fn calls_mut(&mut self) -> &mut Vec<Call>;
18
19 fn calls(&self) -> &Vec<Call>;
21
22 fn add_typed<C: SolCall + Clone>(mut self, to: Address, call: C) -> Self {
24 let typed_call = TypedCall::new(to, call);
25 self.calls_mut().push(Call::new(
26 typed_call.to(),
27 typed_call.value,
28 typed_call.data(),
29 ));
30 self
31 }
32
33 fn add_typed_with_value<C: SolCall + Clone>(
35 mut self,
36 to: Address,
37 call: C,
38 value: U256,
39 ) -> Self {
40 let typed_call = TypedCall::new(to, call).with_value(value);
41 self.calls_mut().push(Call::new(
42 typed_call.to(),
43 typed_call.value,
44 typed_call.data(),
45 ));
46 self
47 }
48
49 fn add_raw(mut self, to: Address, value: U256, data: impl Into<Bytes>) -> Self {
51 self.calls_mut().push(Call::new(to, value, data));
52 self
53 }
54
55 fn add(mut self, call: impl SafeCall) -> Self {
57 self.calls_mut().push(Call {
58 to: call.to(),
59 value: call.value(),
60 data: call.data(),
61 operation: call.operation(),
62 gas_limit: None,
63 });
64 self
65 }
66
67 fn with_gas_limit(self, gas_limit: u64) -> Self;
75
76 fn call_count(&self) -> usize {
78 self.calls().len()
79 }
80
81 fn simulate(self) -> impl Future<Output = Result<Self>> + Send;
90
91 fn simulation_result(&self) -> Option<&SimulationResult>;
93
94 fn simulation_success(self) -> Result<Self>;
105}
106
107pub trait SafeCall {
109 fn to(&self) -> Address;
111
112 fn value(&self) -> U256;
114
115 fn data(&self) -> Bytes;
117
118 fn operation(&self) -> Operation;
120}
121
122#[derive(Debug, Clone)]
124pub struct Call {
125 pub to: Address,
127 pub value: U256,
129 pub data: Bytes,
131 pub operation: Operation,
133 pub gas_limit: Option<u64>,
135}
136
137impl Call {
138 pub fn new(to: Address, value: U256, data: impl Into<Bytes>) -> Self {
140 Self {
141 to,
142 value,
143 data: data.into(),
144 operation: Operation::Call,
145 gas_limit: None,
146 }
147 }
148
149 pub fn call(to: Address, data: impl Into<Bytes>) -> Self {
151 Self::new(to, U256::ZERO, data)
152 }
153
154 pub fn delegate_call(to: Address, data: impl Into<Bytes>) -> Self {
156 Self {
157 to,
158 value: U256::ZERO,
159 data: data.into(),
160 operation: Operation::DelegateCall,
161 gas_limit: None,
162 }
163 }
164
165 pub fn with_operation(mut self, operation: Operation) -> Self {
167 self.operation = operation;
168 self
169 }
170
171 pub fn with_value(mut self, value: U256) -> Self {
173 self.value = value;
174 self
175 }
176
177 pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
179 self.gas_limit = Some(gas_limit);
180 self
181 }
182}
183
184impl SafeCall for Call {
185 fn to(&self) -> Address {
186 self.to
187 }
188
189 fn value(&self) -> U256 {
190 self.value
191 }
192
193 fn data(&self) -> Bytes {
194 self.data.clone()
195 }
196
197 fn operation(&self) -> Operation {
198 self.operation
199 }
200}
201
202#[derive(Debug, Clone)]
204pub struct TypedCall<C: SolCall> {
205 pub to: Address,
207 pub value: U256,
209 pub call: C,
211 pub operation: Operation,
213}
214
215impl<C: SolCall> TypedCall<C> {
216 pub fn new(to: Address, call: C) -> Self {
218 Self {
219 to,
220 value: U256::ZERO,
221 call,
222 operation: Operation::Call,
223 }
224 }
225
226 pub fn with_value(mut self, value: U256) -> Self {
228 self.value = value;
229 self
230 }
231
232 pub fn with_operation(mut self, operation: Operation) -> Self {
234 self.operation = operation;
235 self
236 }
237}
238
239impl<C: SolCall + Clone> SafeCall for TypedCall<C> {
240 fn to(&self) -> Address {
241 self.to
242 }
243
244 fn value(&self) -> U256 {
245 self.value
246 }
247
248 fn data(&self) -> Bytes {
249 self.call.abi_encode().into()
250 }
251
252 fn operation(&self) -> Operation {
253 self.operation
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use alloy::primitives::address;
261
262 #[test]
263 fn test_call_new() {
264 let to = address!("0x1234567890123456789012345678901234567890");
265 let value = U256::from(1000);
266 let data = Bytes::from(vec![0x01, 0x02, 0x03]);
267
268 let call = Call::new(to, value, data.clone());
269
270 assert_eq!(call.to(), to);
271 assert_eq!(call.value(), value);
272 assert_eq!(call.data(), data);
273 assert_eq!(call.operation(), Operation::Call);
274 }
275
276 #[test]
277 fn test_call_delegate() {
278 let to = address!("0x1234567890123456789012345678901234567890");
279 let data = Bytes::from(vec![0x01, 0x02, 0x03]);
280
281 let call = Call::delegate_call(to, data.clone());
282
283 assert_eq!(call.to(), to);
284 assert_eq!(call.value(), U256::ZERO);
285 assert_eq!(call.data(), data);
286 assert_eq!(call.operation(), Operation::DelegateCall);
287 }
288
289 #[test]
290 fn test_call_call_constructor() {
291 let to = address!("0x1234567890123456789012345678901234567890");
292 let data = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]);
293
294 let call = Call::call(to, data.clone());
295
296 assert_eq!(call.to(), to);
297 assert_eq!(call.value(), U256::ZERO);
298 assert_eq!(call.data(), data);
299 assert_eq!(call.operation(), Operation::Call);
300 assert!(call.gas_limit.is_none());
301 }
302
303 #[test]
304 fn test_call_with_operation() {
305 let to = address!("0x1234567890123456789012345678901234567890");
306 let data = Bytes::from(vec![0x01, 0x02]);
307
308 let call = Call::call(to, data).with_operation(Operation::DelegateCall);
310
311 assert_eq!(call.operation(), Operation::DelegateCall);
312 }
313
314 #[test]
315 fn test_call_with_value() {
316 let to = address!("0x1234567890123456789012345678901234567890");
317 let data = Bytes::from(vec![0x01, 0x02]);
318 let value = U256::from(1_000_000_000_000_000_000u128); let call = Call::call(to, data).with_value(value);
321
322 assert_eq!(call.value(), value);
323 }
324
325 #[test]
326 fn test_call_with_gas_limit() {
327 let to = address!("0x1234567890123456789012345678901234567890");
328 let data = Bytes::from(vec![0x01, 0x02]);
329 let gas_limit = 100_000u64;
330
331 let call = Call::call(to, data).with_gas_limit(gas_limit);
332
333 assert_eq!(call.gas_limit, Some(gas_limit));
334 }
335
336 #[test]
337 fn test_call_chained_builders() {
338 let to = address!("0x1234567890123456789012345678901234567890");
339 let data = Bytes::from(vec![0xab, 0xcd]);
340 let value = U256::from(5000);
341 let gas_limit = 50_000u64;
342
343 let call = Call::call(to, data.clone())
344 .with_value(value)
345 .with_operation(Operation::Call)
346 .with_gas_limit(gas_limit);
347
348 assert_eq!(call.to(), to);
349 assert_eq!(call.value(), value);
350 assert_eq!(call.data(), data);
351 assert_eq!(call.operation(), Operation::Call);
352 assert_eq!(call.gas_limit, Some(gas_limit));
353 }
354
355 #[test]
356 fn test_typed_call_new() {
357 use alloy::sol;
358
359 sol! {
361 interface TestToken {
362 function transfer(address to, uint256 amount) external returns (bool);
363 }
364 }
365
366 let token_address = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
367 let recipient = address!("0x9999999999999999999999999999999999999999");
368 let amount = U256::from(1000);
369
370 let transfer_call = TestToken::transferCall {
371 to: recipient,
372 amount,
373 };
374
375 let typed_call = TypedCall::new(token_address, transfer_call);
376
377 assert_eq!(typed_call.to(), token_address);
378 assert_eq!(typed_call.value(), U256::ZERO);
379 assert_eq!(typed_call.operation(), Operation::Call);
380 let data = typed_call.data();
382 assert_eq!(&data[..4], &[0xa9, 0x05, 0x9c, 0xbb]);
383 }
384
385 #[test]
386 fn test_typed_call_with_value() {
387 use alloy::sol;
388
389 sol! {
390 interface TestContract {
391 function deposit() external payable;
392 }
393 }
394
395 let contract_address = address!("0x1234567890123456789012345678901234567890");
396 let deposit_call = TestContract::depositCall {};
397 let value = U256::from(1_000_000_000_000_000_000u128); let typed_call = TypedCall::new(contract_address, deposit_call).with_value(value);
400
401 assert_eq!(typed_call.value(), value);
402 }
403
404 #[test]
405 fn test_typed_call_with_operation() {
406 use alloy::sol;
407
408 sol! {
409 interface TestMultiSend {
410 function multiSend(bytes calldata transactions) external;
411 }
412 }
413
414 let multisend_address = address!("0x1234567890123456789012345678901234567890");
415 let multisend_call = TestMultiSend::multiSendCall {
416 transactions: Bytes::from(vec![0x01, 0x02, 0x03]),
417 };
418
419 let typed_call = TypedCall::new(multisend_address, multisend_call)
420 .with_operation(Operation::DelegateCall);
421
422 assert_eq!(typed_call.operation(), Operation::DelegateCall);
423 }
424
425 #[test]
426 fn test_typed_call_safe_call_trait() {
427 use alloy::sol;
428
429 sol! {
430 interface TestToken {
431 function approve(address spender, uint256 amount) external returns (bool);
432 }
433 }
434
435 let token_address = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
436 let spender = address!("0x1111111111111111111111111111111111111111");
437 let amount = U256::MAX;
438
439 let approve_call = TestToken::approveCall { spender, amount };
440
441 let typed_call = TypedCall::new(token_address, approve_call);
442
443 assert_eq!(typed_call.to(), token_address);
445 assert_eq!(typed_call.value(), U256::ZERO);
446 assert_eq!(typed_call.operation(), Operation::Call);
447 let data = typed_call.data();
449 assert_eq!(&data[..4], &[0x09, 0x5e, 0xa7, 0xb3]);
450 }
451}