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