Sandwich Inspector
The Sandwich Inspector is designed to detect and analyze the profitability of Sandwich attacks.
What is a Sandwich Attack?
A Sandwich attack unfolds in three steps:
- Front-run: An attacker buys an asset just before a victim's transaction, raising its market price.
- Victim Transaction: The victim purchases the asset at the inflated price.
- Back-run: The attacker sells the asset post-victim transaction, profiting from the price difference.
Methodology
Step 1: Retrieve Relevant Transactions
The inspector retrieves transactions in the block that involve swap
, transfer
, eth_transfer
, FlashLoan
, batch_swap
or aggregator_swap
actions.
Step 2: Identify Potential Sandwiches
We run two functions to find possible sandwich attacks:
- One checks for repeated transactions from the same account:
get_possible_sandwich_duplicate_senders
- The other looks for repeated calls to the same contract:
get_possible_sandwich_duplicate_contracts
We use the PossibleSandwich
type to represent each potential attack:
pub struct PossibleSandwich {
pub eoa: Address,
pub possible_frontruns: Vec<B256>,
pub possible_backrun: B256,
pub mev_executor_contract: Address,
// Mapping of possible frontruns to the set of possible
// victims. By definition the victims of latter transactions
// can also be victims of the former
pub victims: Vec<Vec<B256>>,
}
This type holds the attacker's address, frontrun and backrun transactions, the contract used, and sets of victim transactions grouped by frontrun.
How It Works
Our algorithm constructs the largest possible sandwich scenarios by identifying duplicate addresses. Here's the process:
-
Track Duplicates
- Map addresses (contract or EOA) to their most recent transaction hash
-
Build Victim Sets
- For each transaction, track potential victims (transactions that occur after it)
-
Construct a
PossibleSandwich
-
When we encounter a duplicate address, we create or update a
PossibleSandwich
:a) For the first duplicate:
- Create a new PossibleSandwich
- Set the previous transaction as the frontrun
- Set the current transaction as the backrun
- Add intervening transactions as victims
b) For subsequent duplicates:
- Add the previous transaction to possible frontruns
- Update the backrun to the current transaction
- Add the new set of victims
-
The Result
This step yields a list of PossibleSandwich
. Each represents a potential sandwich attack, from simple to complex.
We catch what we call "Big Mac" sandwiches - attacks with multiple frontrun transactions, each targeting its own set of victims. Read more about these complex patterns.
We remove duplicates from our list. What remains are the largest, most comprehensive sandwich scenarios in the block. This wide-net approach ensures we don't miss any potential sandwiches, no matter how intricate. We'll analyze the details in later steps.
Step 3: Partitioning Possible Sandwiches
Here's how partitioning works:
- We iterate through victim sets in each sandwich.
- Empty victim sets signal a break in the sandwich.
- We create new
PossibleSandwich
structs at these breaks.
Note: Our partitioning assumes attackers maximize efficiency. Multiple attacker transactions without intervening victims may lead to unexpected results. If you find examples breaking this assumption, please report them for a bounty.
Step 4: Analyze Possible Sandwich Attacks
Pool Overlap Check
Front-run and back-run transactions must swap on at least one common liquidity pool.
Victim Verification
After confirming pool overlap, we validate interleaved transactions as victims:
- Group victim transactions by EOA to account for multi-step victim operations (e.g., approval and swap).
- An EOA is a victim if it:
- Swaps on the same pool and direction as the front-run
- Swaps on the same pool and opposite direction as the back-run
A PossibleSandwich
is confirmed if:
- At least 50% of EOAs are considered victims
- At least one complete sandwich is detected (e.g a victim swap overlaps with both front-run and back-run in pool & direction)
If confirmed, we proceed to Step 5. Otherwise, we initiate recursive verification.
Recursive Sandwich Verification
For unconfirmed sandwiches, we employ a recursive strategy to explore all possible transaction combinations:
-
The process stops after 6 recursive iterations.
-
We apply two types of "shrinking":
Back Shrink:
- Remove the last victim set
- Use the last front-run as the new back-run
- Recalculate the sandwich (run step 4 on the new sandwich)
Front Shrink:
- Remove the first victim set
- Remove the first front-run transaction
- Retain the original back-run
- Recalculate the sandwich (run step 4 on the new sandwich)
-
We continue this process as long as:
- There's more than one front-run transaction
- Victim sets aren't empty
- At least one victim has non-empty swaps or transfers
Step 5: Calculate Sandwich PnL
For confirmed sandwiches:
- Calculate searcher revenue: Balance deltas of searcher addresses & sibling address (e.g piggy bank address) if applicable
- Calculate searcher cost: Sum of gas costs for all attacker transactions
- Profit = Revenue - Cost