use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
use alloy_primitives::Address;
#[derive(Clone, Debug, Default)]
pub struct Prank {
pub prank_caller: Address,
pub prank_origin: Address,
pub new_caller: Address,
pub new_origin: Option<Address>,
pub depth: u64,
pub single_call: bool,
pub delegate_call: bool,
pub used: bool,
}
impl Prank {
pub fn new(
prank_caller: Address,
prank_origin: Address,
new_caller: Address,
new_origin: Option<Address>,
depth: u64,
single_call: bool,
delegate_call: bool,
) -> Self {
Self {
prank_caller,
prank_origin,
new_caller,
new_origin,
depth,
single_call,
delegate_call,
used: false,
}
}
pub fn first_time_applied(&self) -> Option<Self> {
if self.used {
None
} else {
Some(Self { used: true, ..self.clone() })
}
}
}
impl Cheatcode for prank_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender } = self;
prank(ccx, msgSender, None, true, false)
}
}
impl Cheatcode for startPrank_0Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender } = self;
prank(ccx, msgSender, None, false, false)
}
}
impl Cheatcode for prank_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, txOrigin } = self;
prank(ccx, msgSender, Some(txOrigin), true, false)
}
}
impl Cheatcode for startPrank_1Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, txOrigin } = self;
prank(ccx, msgSender, Some(txOrigin), false, false)
}
}
impl Cheatcode for prank_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, delegateCall } = self;
prank(ccx, msgSender, None, true, *delegateCall)
}
}
impl Cheatcode for startPrank_2Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, delegateCall } = self;
prank(ccx, msgSender, None, false, *delegateCall)
}
}
impl Cheatcode for prank_3Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, txOrigin, delegateCall } = self;
prank(ccx, msgSender, Some(txOrigin), true, *delegateCall)
}
}
impl Cheatcode for startPrank_3Call {
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
let Self { msgSender, txOrigin, delegateCall } = self;
prank(ccx, msgSender, Some(txOrigin), false, *delegateCall)
}
}
impl Cheatcode for stopPrankCall {
fn apply(&self, state: &mut Cheatcodes) -> Result {
let Self {} = self;
state.prank = None;
Ok(Default::default())
}
}
fn prank(
ccx: &mut CheatsCtxt,
new_caller: &Address,
new_origin: Option<&Address>,
single_call: bool,
delegate_call: bool,
) -> Result {
let prank = Prank::new(
ccx.caller,
ccx.ecx.env.tx.caller,
*new_caller,
new_origin.copied(),
ccx.ecx.journaled_state.depth(),
single_call,
delegate_call,
);
if delegate_call {
let code = ccx.code(*new_caller)?;
ensure!(!code.is_empty(), "cannot `prank` delegate call from an EOA");
}
if let Some(Prank { used, single_call: current_single_call, .. }) = ccx.state.prank {
ensure!(used, "cannot overwrite a prank until it is applied at least once");
ensure!(
single_call == current_single_call,
"cannot override an ongoing prank with a single vm.prank; \
use vm.startPrank to override the current prank"
);
}
ensure!(
ccx.state.broadcast.is_none(),
"cannot `prank` for a broadcasted transaction; \
pass the desired `tx.origin` into the `broadcast` cheatcode call"
);
ccx.state.prank = Some(prank);
Ok(Default::default())
}