1use crate::{Cheatcode, CheatsCtxt, Result, Vm::*};
2use alloy_primitives::Address;
3
4#[derive(Clone, Copy, Debug, Default)]
6pub struct Prank {
7 pub prank_caller: Address,
9 pub prank_origin: Address,
11 pub new_caller: Address,
13 pub new_origin: Option<Address>,
15 pub depth: u64,
17 pub single_call: bool,
19 pub delegate_call: bool,
21 pub used: bool,
23}
24
25impl Prank {
26 pub fn new(
28 prank_caller: Address,
29 prank_origin: Address,
30 new_caller: Address,
31 new_origin: Option<Address>,
32 depth: u64,
33 single_call: bool,
34 delegate_call: bool,
35 ) -> Self {
36 Self {
37 prank_caller,
38 prank_origin,
39 new_caller,
40 new_origin,
41 depth,
42 single_call,
43 delegate_call,
44 used: false,
45 }
46 }
47
48 pub fn first_time_applied(&self) -> Option<Self> {
51 if self.used {
52 None
53 } else {
54 Some(Self { used: true, ..*self })
55 }
56 }
57}
58
59impl Cheatcode for prank_0Call {
60 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
61 let Self { msgSender } = self;
62 prank(ccx, msgSender, None, true, false)
63 }
64}
65
66impl Cheatcode for startPrank_0Call {
67 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
68 let Self { msgSender } = self;
69 prank(ccx, msgSender, None, false, false)
70 }
71}
72
73impl Cheatcode for prank_1Call {
74 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
75 let Self { msgSender, txOrigin } = self;
76 prank(ccx, msgSender, Some(txOrigin), true, false)
77 }
78}
79
80impl Cheatcode for startPrank_1Call {
81 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
82 let Self { msgSender, txOrigin } = self;
83 prank(ccx, msgSender, Some(txOrigin), false, false)
84 }
85}
86
87impl Cheatcode for prank_2Call {
88 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
89 let Self { msgSender, delegateCall } = self;
90 prank(ccx, msgSender, None, true, *delegateCall)
91 }
92}
93
94impl Cheatcode for startPrank_2Call {
95 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
96 let Self { msgSender, delegateCall } = self;
97 prank(ccx, msgSender, None, false, *delegateCall)
98 }
99}
100
101impl Cheatcode for prank_3Call {
102 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
103 let Self { msgSender, txOrigin, delegateCall } = self;
104 prank(ccx, msgSender, Some(txOrigin), true, *delegateCall)
105 }
106}
107
108impl Cheatcode for startPrank_3Call {
109 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
110 let Self { msgSender, txOrigin, delegateCall } = self;
111 prank(ccx, msgSender, Some(txOrigin), false, *delegateCall)
112 }
113}
114
115impl Cheatcode for stopPrankCall {
116 fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
117 let Self {} = self;
118 ccx.state.pranks.remove(&ccx.ecx.journaled_state.depth());
119 Ok(Default::default())
120 }
121}
122
123fn prank(
124 ccx: &mut CheatsCtxt,
125 new_caller: &Address,
126 new_origin: Option<&Address>,
127 single_call: bool,
128 delegate_call: bool,
129) -> Result {
130 if delegate_call {
132 let code = ccx.code(*new_caller)?;
133 ensure!(!code.is_empty(), "cannot `prank` delegate call from an EOA");
134 }
135
136 let depth = ccx.ecx.journaled_state.depth();
137 if let Some(Prank { used, single_call: current_single_call, .. }) = ccx.state.get_prank(depth) {
138 ensure!(used, "cannot overwrite a prank until it is applied at least once");
139 ensure!(
142 single_call == *current_single_call,
143 "cannot override an ongoing prank with a single vm.prank; \
144 use vm.startPrank to override the current prank"
145 );
146 }
147
148 let prank = Prank::new(
149 ccx.caller,
150 ccx.ecx.env.tx.caller,
151 *new_caller,
152 new_origin.copied(),
153 depth,
154 single_call,
155 delegate_call,
156 );
157
158 ensure!(
159 ccx.state.broadcast.is_none(),
160 "cannot `prank` for a broadcasted transaction; \
161 pass the desired `tx.origin` into the `broadcast` cheatcode call"
162 );
163
164 ccx.state.pranks.insert(prank.depth, prank);
165 Ok(Default::default())
166}