Blockchain Security
Front-Running and MEV: How to Protect Your Smart Contracts
TL;DR
Front-running is the single most pervasive threat in DeFi that most developers never test for. Every unprotected swap, every on-chain auction, every liquidation call — all of them broadcast intent to the mempool before execution, giving MEV bots milliseconds to extract value from your users. I check for front-running vulnerabilities in every DeFi audit I perform, and I find exploitable patterns in the majority of them. The defenses are well understood: commit-reveal schemes hide intent until execution, Flashbots Protect routes transactions through private channels that bypass the public mempool, slippage guards cap the damage a sandwich attack can inflict, and private mempools eliminate the information leak entirely. This article walks through how these attacks work at the transaction level, dissects sandwich attacks step by step, and gives you the Solidity patterns and infrastructure choices I enforce in every audit.
What Front-Running Is
Front-running in traditional finance is illegal. A broker sees your large buy order, buys the asset first, then lets your order push the price up, and sells for a profit. Simple. Unethical. Criminal.
In DeFi, the same economic behavior is not only possible — it is automated, permissionless, and operating at scale. The difference is that there is no broker. There is the mempool.
When you submit a transaction on Ethereum, it does not execute immediately. It enters the mempool — a public waiting area where pending transactions sit until a block producer includes them in the next block. Every transaction in the mempool is visible to anyone watching. Your swap on Uniswap, your liquidation call on Aave, your bid on an NFT auction — all of it is broadcast to the network before it executes.
Front-running in DeFi means observing a pending transaction in the mempool, understanding the profit opportunity it creates, and submitting a competing transaction with a higher gas price to get included first. The front-runner extracts value that would have gone to the original user.
This is not a bug in any specific protocol. It is a structural property of how blockchains order transactions. Block producers choose which transactions to include and in what order, and the default ordering criterion is gas price — highest bidder goes first. That single design choice created an entire economy around transaction ordering exploitation.
The term for this economy is MEV: Maximal Extractable Value. Originally called Miner Extractable Value when Ethereum used proof-of-work, the name evolved after the merge to proof-of-stake, but the mechanics remain identical. MEV is the total value that can be extracted by reordering, inserting, or censoring transactions within a block.
In every security audit I perform on DeFi protocols, front-running analysis is not optional. It is the first thing I check after access control and reentrancy.
How MEV Bots Work
MEV extraction is not ad hoc. It is a sophisticated, highly competitive industry with its own infrastructure, strategies, and economic dynamics. Understanding how MEV bots operate is essential to defending against them.
The Architecture
A typical MEV bot consists of four components:
Mempool monitoring. The bot runs a node (or connects to multiple nodes) and subscribes to pending transactions in real time. Every new transaction is decoded — the bot reads the function selector, parses the calldata, and determines what the transaction will do.
Simulation engine. Before committing capital, the bot simulates the target transaction against the current blockchain state using a local fork. It calculates the exact price impact, the exact profit opportunity, and the exact gas cost. If the simulation shows profit, the bot proceeds. If not, it moves to the next opportunity.
Bundle construction. The bot constructs a bundle of transactions — its front-run transaction, the victim's transaction, and optionally a back-run transaction — and submits them as an atomic unit. If any transaction in the bundle fails, the entire bundle reverts. This eliminates execution risk.
Submission layer. On Ethereum mainnet, most sophisticated bots submit bundles through Flashbots or similar block builder infrastructure, which guarantees atomic inclusion. On L2s and alternative L1s, bots may still use gas price bidding or chain-specific mechanisms.
The Economics
MEV extraction is a zero-sum game played at nanosecond speeds. The profit margin on a single sandwich attack might be $10, but a bot processing thousands of transactions per day accumulates significant revenue. The competition is fierce — multiple bots detect the same opportunity simultaneously, and only one can win.
This competition creates what researchers call priority gas auctions (PGAs). Bots outbid each other in gas price, driving up transaction costs for everyone on the network. The gas consumed by failed MEV transactions is waste — pure deadweight loss for the ecosystem.
Here are the numbers that matter: since the merge, MEV bots on Ethereum have extracted over $600 million in cumulative value. That is $600 million taken directly from users who submitted transactions expecting fair execution and received worse prices instead.
Sandwich Attacks Explained
The sandwich attack is the most common and most damaging form of MEV extraction. It accounts for roughly 60% of all MEV revenue on Ethereum. Every DeFi user who has swapped tokens on a DEX without protection has been a potential sandwich target.
Here is how it works, step by step.
Step 1: Detection. Alice submits a transaction to swap 10 ETH for USDC on Uniswap. This transaction enters the mempool. The MEV bot decodes the calldata and sees that Alice is buying USDC with 10 ETH. It also sees Alice's slippage tolerance — the minimum amount of USDC she is willing to accept.
Step 2: Front-run. The bot submits its own transaction to buy USDC with ETH, using a higher gas price than Alice's transaction. This transaction executes first, pushing the USDC price up.
Step 3: Victim execution. Alice's transaction executes at the now-inflated price. She receives fewer USDC than she would have without the bot's interference. But she still receives more than her slippage tolerance, so the transaction does not revert.
Step 4: Back-run. The bot immediately sells the USDC it bought in step 2, at the higher price established by Alice's large buy. The bot profits from the price difference, minus gas costs.
In a single atomic bundle, the bot has extracted value from Alice by manipulating the price around her transaction. Alice got worse execution. The bot got the difference. The liquidity providers saw two extra trades that cancel out. And the block builder received a tip from the bot for including the bundle.
The math is deterministic. If Alice's trade has a price impact of 0.3% and her slippage tolerance is 1%, the bot can extract up to 0.7% of Alice's trade value. On a $100,000 swap, that is $700 taken from Alice.
Victim's swap: 10 ETH -> USDC (expects ~$25,000 USDC)
Bot front-run: 5 ETH -> USDC (pushes price up 0.5%)
Victim executes: 10 ETH -> $24,875 USDC (got $125 less)
Bot back-run: sells USDC -> 5.025 ETH (pockets ~$62 profit after gas)The attack is invisible to Alice unless she checks the block explorer after her transaction confirms. She submitted a swap, received USDC, and moved on. She never knew a bot extracted value from her trade.
Common Vulnerable Patterns
After auditing dozens of DeFi protocols, I have catalogued the patterns that are most frequently vulnerable to front-running. If your code matches any of these, assume it is exploitable.
Unprotected Swaps
Any function that executes a swap on a DEX without enforcing a minimum output amount is a free lunch for sandwich bots.
// VULNERABLE: No minimum output, no deadline
function swapTokens(address tokenIn, uint256 amountIn) external {
router.swapExactTokensForTokens(
amountIn,
0, // amountOutMin = 0, accepts ANY output
path,
msg.sender,
block.timestamp // deadline is current block, meaningless
);
}Setting amountOutMin to zero tells the DEX router that you will accept any amount of output tokens, including one wei. A sandwich bot will take everything above one wei.
On-Chain Auctions
Any auction where bids are visible before settlement is front-runnable. This includes NFT auctions, liquidation auctions, and Dutch auctions where the current price is computable from public state.
// VULNERABLE: Bid amount visible in mempool
function placeBid(uint256 auctionId) external payable {
require(msg.value > highestBid[auctionId], "Bid too low");
highestBid[auctionId] = msg.value;
highestBidder[auctionId] = msg.sender;
}A front-runner sees your bid in the mempool and submits a bid exactly one wei higher.
Liquidation Calls
Protocols that allow anyone to trigger liquidations create a race condition. The liquidation profit is visible on-chain (undercollateralized positions are public state), and multiple bots compete to be first.
Oracle Updates
When a protocol uses an oracle that updates in a predictable pattern (for example, Chainlink updates when price deviates by a threshold), bots can front-run the oracle update to trade on the new price before other users see it. This is sometimes called oracle front-running or oracle extractable value (OEV).
Governance Votes
Even governance is not immune. If a proposal will change a protocol parameter (for example, increasing a collateral factor), a bot can front-run the governance execution by depositing collateral and borrowing the maximum amount at the new, more favorable ratio.
Defense 1 — Commit-Reveal Schemes
The commit-reveal pattern is the most fundamental defense against front-running. It works by splitting a user action into two phases: a commit phase where the user submits a hash of their intent without revealing the details, and a reveal phase where the user provides the actual parameters and the contract verifies the hash.
The key insight is that a bot cannot front-run what it cannot read. If Alice commits keccak256(abi.encodePacked(bid, salt)) instead of revealing her actual bid, the bot sees only an opaque hash in the mempool. It has no information to exploit.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract CommitRevealAuction {
struct Commit {
bytes32 hash;
uint64 timestamp;
bool revealed;
}
uint256 public constant REVEAL_WINDOW = 1 hours;
uint256 public constant COMMIT_WINDOW = 24 hours;
mapping(address => Commit) public commits;
mapping(uint256 => address) public highestBidder;
mapping(uint256 => uint256) public highestBid;
event BidCommitted(address indexed bidder, bytes32 hash);
event BidRevealed(address indexed bidder, uint256 amount);
/// @notice Phase 1: Submit a hash of your bid
/// @param commitHash keccak256(abi.encodePacked(auctionId, amount, salt))
function commitBid(bytes32 commitHash) external {
commits[msg.sender] = Commit({
hash: commitHash,
timestamp: uint64(block.timestamp),
revealed: false
});
emit BidCommitted(msg.sender, commitHash);
}
/// @notice Phase 2: Reveal your bid after commit window closes
function revealBid(
uint256 auctionId,
uint256 amount,
bytes32 salt
) external payable {
Commit storage c = commits[msg.sender];
require(c.hash != bytes32(0), "No commit found");
require(!c.revealed, "Already revealed");
require(
block.timestamp >= c.timestamp + COMMIT_WINDOW,
"Commit window still open"
);
require(
block.timestamp <= c.timestamp + COMMIT_WINDOW + REVEAL_WINDOW,
"Reveal window expired"
);
bytes32 expectedHash = keccak256(
abi.encodePacked(auctionId, amount, salt)
);
require(c.hash == expectedHash, "Hash mismatch");
require(msg.value == amount, "Incorrect ETH sent");
c.revealed = true;
if (amount > highestBid[auctionId]) {
// Refund previous highest bidder
if (highestBidder[auctionId] != address(0)) {
payable(highestBidder[auctionId]).transfer(
highestBid[auctionId]
);
}
highestBid[auctionId] = amount;
highestBidder[auctionId] = msg.sender;
} else {
// Refund losing bid
payable(msg.sender).transfer(amount);
}
emit BidRevealed(msg.sender, amount);
}
}When to Use Commit-Reveal
Commit-reveal is ideal for auctions, governance votes, and any mechanism where the action itself reveals exploitable information. It is not suitable for DEX swaps — users expect instant execution, and a two-phase process adds unacceptable latency and UX friction.
Limitations
The pattern requires two transactions, which means double the gas cost and a worse user experience. It also introduces a timing game — if the reveal window is too short, legitimate users might miss it. If it is too long, it delays settlement. I typically recommend a 24-hour commit window followed by a 1-hour reveal window for auctions, and shorter windows for time-sensitive mechanisms.
Defense 2 — Flashbots Protect
Flashbots Protect is the most practical defense for end users. It works at the infrastructure layer, bypassing the public mempool entirely.
When a user submits a transaction through Flashbots Protect (by adding a custom RPC endpoint to their wallet), the transaction is sent directly to Flashbots' block builders instead of being broadcast to the public mempool. Since the transaction never enters the public mempool, mempool-scanning bots never see it.
How to Integrate Flashbots Protect
For frontend dApps, the integration is straightforward. You configure the user's wallet provider to use the Flashbots RPC endpoint:
const FLASHBOTS_RPC = "https://rpc.flashbots.net";
async function addFlashbotsNetwork(): Promise<void> {
if (!window.ethereum) return;
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [
{
chainId: "0x1",
chainName: "Ethereum (Flashbots Protect)",
rpcUrls: [FLASHBOTS_RPC],
nativeCurrency: {
name: "Ether",
symbol: "ETH",
decimals: 18,
},
blockExplorerUrls: ["https://etherscan.io"],
},
],
});
}For backend systems or bots, you can submit transactions directly to the Flashbots relay:
import { Wallet, JsonRpcProvider } from "ethers";
import { FlashbotsBundleProvider } from "@flashbots/ethers-provider-bundle";
async function sendProtectedTransaction(
signedTx: string
): Promise<string> {
const provider = new JsonRpcProvider("https://rpc.flashbots.net");
const authSigner = new Wallet(process.env.FLASHBOTS_AUTH_KEY!);
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider,
authSigner
);
const result = await flashbotsProvider.sendRawBundle(
[signedTx],
await provider.getBlockNumber() + 1
);
return result.bundleHash;
}Limitations
Flashbots Protect only works on Ethereum mainnet and a limited number of other chains. It does not protect against block builder-level MEV extraction (the builder who receives your transaction could theoretically still extract MEV, though Flashbots builders commit to not doing so). And it requires users to change their RPC configuration, which is a UX barrier.
For L2s like Arbitrum and Optimism, the sequencer is the single block producer, and transaction ordering is first-come-first-served by default. This eliminates gas-price-based front-running but introduces sequencer-level MEV risk, which is a different threat model.
Defense 3 — Slippage Guards
Slippage protection is the minimum viable defense that every DEX integration must implement. It does not prevent front-running — it limits the damage.
A slippage guard sets a minimum acceptable output for a swap. If the actual output falls below this minimum (because of a sandwich attack, natural price movement, or any other cause), the transaction reverts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
contract ProtectedSwapper {
ISwapRouter public immutable swapRouter;
uint256 public constant MAX_SLIPPAGE_BPS = 50; // 0.5% max slippage
constructor(address _router) {
swapRouter = ISwapRouter(_router);
}
/// @notice Execute a swap with enforced slippage protection
/// @param tokenIn Input token address
/// @param tokenOut Output token address
/// @param amountIn Amount of input token
/// @param expectedOut Expected output from off-chain quote
/// @param deadline Transaction deadline timestamp
function protectedSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 expectedOut,
uint256 deadline
) external returns (uint256 amountOut) {
require(deadline > block.timestamp, "Transaction expired");
uint256 minOut = (expectedOut * (10_000 - MAX_SLIPPAGE_BPS)) / 10_000;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: 3000,
recipient: msg.sender,
deadline: deadline,
amountIn: amountIn,
amountOutMinimum: minOut,
sqrtPriceLimitX96: 0
});
amountOut = swapRouter.exactInputSingle(params);
}
}The Deadline Parameter
Slippage guards are incomplete without a transaction deadline. Without a deadline, a pending transaction can sit in the mempool for hours or days, during which the market price may move dramatically. When the transaction finally executes, it might pass the slippage check at a price that is far worse than what the user intended.
Always set the deadline to a short window — 5 to 20 minutes from submission. If the transaction has not been included by then, the user should resubmit with fresh price data.
Calculating the Right Slippage Tolerance
The slippage tolerance is a tradeoff. Too tight, and legitimate transactions revert during normal volatility. Too loose, and sandwich bots extract more value.
My recommendations based on auditing production protocols:
- Stablecoin pairs (USDC/USDT): 0.1% to 0.3%
- Major pairs (ETH/USDC): 0.3% to 0.5%
- Mid-cap tokens: 0.5% to 1.0%
- Low-liquidity tokens: 1.0% to 3.0%, but warn the user
Never set slippage tolerance based on a percentage alone. Calculate it from the expected price impact of the trade size against the pool's liquidity depth. A $100 swap in a $100M pool needs 0.1% tolerance. A $100,000 swap in the same pool might need 0.5%.
Defense 4 — Private Mempools
Private mempools represent the infrastructure-level solution to MEV. Instead of broadcasting transactions to the entire network, transactions are sent through a private channel to a block builder who includes them without exposing them to the public mempool.
How Private Mempools Work
In post-merge Ethereum, the block production pipeline works like this:
- Users submit transactions to the mempool (public or private)
- Searchers scan for MEV opportunities and construct bundles
- Block builders assemble blocks from transactions and bundles
- Proposers (validators) select the most valuable block and propose it
Private mempools intervene at step 1. By routing transactions directly to a trusted builder, you skip the public mempool entirely. The builder includes your transaction in a block without exposing it to searchers.
Available Solutions
Flashbots Protect (covered above) is the most established. It routes transactions to the Flashbots builder network.
MEV Blocker by CoW Protocol offers an alternative with a unique twist — if your transaction does create MEV, the extracted value is partially rebated to you instead of going entirely to the searcher. This "MEV kickback" model aligns incentives better than pure protection.
Private RPCs from node providers. Services like Alchemy and Infura offer private transaction submission endpoints. These send your transaction to a limited set of builders rather than broadcasting it publicly.
Implementation for dApp Developers
The cleanest approach is to abstract the RPC selection into your dApp's transaction layer:
interface ProtectedRpcConfig {
chainId: number;
rpcUrl: string;
name: string;
}
const PROTECTED_RPCS: Record<number, ProtectedRpcConfig> = {
1: {
chainId: 1,
rpcUrl: "https://rpc.flashbots.net",
name: "Flashbots Protect",
},
};
function getProtectedProvider(chainId: number): JsonRpcProvider | null {
const config = PROTECTED_RPCS[chainId];
if (!config) return null;
return new JsonRpcProvider(config.rpcUrl);
}The limitation of all private mempool solutions is trust. You are trusting the builder not to extract MEV from your transaction. The Flashbots builder has a strong reputation and economic incentive to maintain trust, but it is not a trustless guarantee. This is an active area of research — encrypted mempools and threshold decryption schemes aim to provide cryptographic guarantees instead of reputational ones.
Testing for Front-Running Vulnerabilities
Most developers test whether their code works. Few test whether their code is front-runnable. Here is the methodology I use in every audit.
Step 1: Identify Value-Bearing Transactions
Map every function in the protocol that moves economic value. Swaps, deposits, withdrawals, liquidations, auctions, reward claims, governance executions. Each of these is a potential MEV target.
Step 2: Simulate Mempool Visibility
For each value-bearing function, ask: if a bot saw this transaction in the mempool, what information would it learn? Can it decode the calldata? Can it simulate the state change? Can it compute the profit opportunity?
Step 3: Construct Attack Scenarios
Write a Foundry test that simulates a sandwich attack:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test, console2} from "forge-std/Test.sol";
contract SandwichTest is Test {
address public victim = makeAddr("victim");
address public attacker = makeAddr("attacker");
function test_sandwichAttackProfitability() public {
// 1. Record state before attack
uint256 victimExpectedOutput = _quoteSwap(10 ether);
// 2. Attacker front-runs
vm.prank(attacker);
uint256 attackerBought = _executeSwap(attacker, 5 ether);
// 3. Victim's swap executes at worse price
vm.prank(victim);
uint256 victimActualOutput = _executeSwap(victim, 10 ether);
// 4. Attacker back-runs
vm.prank(attacker);
uint256 attackerProfit = _executeSell(attacker, attackerBought);
// 5. Assert the attack was profitable
uint256 victimLoss = victimExpectedOutput - victimActualOutput;
console2.log("Victim loss:", victimLoss);
console2.log("Attacker profit:", attackerProfit);
// If this assertion passes, the protocol is vulnerable
assertGt(attackerProfit, 0, "Sandwich attack should not be profitable");
}
function _quoteSwap(uint256 amountIn) internal view returns (uint256) {
// Implement quote logic against your DEX/protocol
}
function _executeSwap(
address user,
uint256 amountIn
) internal returns (uint256) {
// Implement swap execution
}
function _executeSell(
address user,
uint256 amountIn
) internal returns (uint256) {
// Implement sell execution
}
}Step 4: Verify Defenses
For each attack scenario, verify that the protocol's defenses prevent profitability. The slippage guard should cause the victim's transaction to revert when the sandwich inflates the price beyond tolerance. The commit-reveal should prevent the attacker from reading the victim's parameters.
Step 5: Edge Cases
Test with extreme values. What happens when slippage is set to the maximum? What happens when a commit-reveal has a very short window? What happens when multiple bots attack the same transaction? These edge cases reveal weaknesses that normal testing misses.
The MEV Landscape in 2026
The MEV ecosystem has evolved significantly since the early days of gas price bidding wars. Here is where things stand and where they are heading.
Proposer-Builder Separation Is Standard
PBS is now the default on Ethereum. Validators no longer build their own blocks — they outsource block construction to specialized builders who compete to create the most valuable block. This separation has centralized block building (a small number of builders produce the majority of blocks) but has also created a more structured MEV market.
MEV-Share and Order Flow Auctions
The most important development is the shift toward MEV redistribution. Instead of MEV flowing entirely to searchers and builders, protocols like MEV-Share allow users to capture a portion of the MEV their transactions generate. This does not eliminate MEV — it redistributes it more fairly.
Encrypted Mempools
Several teams are building encrypted mempool solutions using threshold encryption and trusted execution environments (TEEs). The idea is simple: transactions are encrypted before submission, and only decrypted after they are committed to a block ordering. This would provide cryptographic front-running protection without requiring trust in any single builder.
Shutter Network and SUAVE (by Flashbots) are the leading efforts. Neither is production-ready for general use as of early 2026, but both are running testnets and demonstrating feasibility.
L2 MEV Is the New Frontier
As activity migrates to L2s, so does MEV. On L2s with centralized sequencers (Arbitrum, Optimism, Base), the sequencer has absolute power over transaction ordering. Today, these sequencers operate on a first-come-first-served basis, which limits gas-price-based MEV but does not eliminate latency-based MEV or sequencer-extractable value.
The decentralization of L2 sequencers — an active research area — will reintroduce competitive block building on L2s, and with it, the full MEV toolkit. Protocols building on L2s should prepare their defenses now.
What This Means for Developers
The defensive strategies in this article remain valid regardless of how the MEV landscape evolves. Commit-reveal hides intent. Slippage guards limit damage. Private mempools reduce exposure. These are protocol-level defenses that work independently of the block production pipeline.
But the landscape is shifting toward a world where MEV is not eliminated but managed. Protocols that integrate with MEV-Share or similar order flow auctions can turn MEV from a pure cost into a partial revenue stream. This is the pragmatic direction — not fighting MEV, but capturing it.
Key Takeaways
- Every unprotected transaction is a target. If your protocol executes swaps, auctions, or liquidations without front-running defenses, MEV bots are already extracting value from your users.
- Slippage protection is the minimum. Never set
amountOutMinto zero. Never omit a transaction deadline. Calculate slippage tolerance from pool liquidity depth, not arbitrary percentages.
- Commit-reveal for auctions. Any mechanism where the action reveals exploitable information — bids, votes, large trades — should use a commit-reveal pattern to hide intent until settlement.
- Private transaction submission for users. Integrate Flashbots Protect or MEV Blocker as a default RPC option in your dApp. One configuration change eliminates the majority of mempool-based MEV extraction.
- Test for front-running explicitly. Write Foundry tests that simulate sandwich attacks. If the attack is profitable in your test environment, it is profitable on mainnet.
- Monitor the MEV landscape. Encrypted mempools, MEV-Share, and L2 sequencer decentralization are changing the rules. Build defenses that work today, but design architectures that can adopt tomorrow's solutions.
- Defense in depth. No single defense is sufficient. Layer slippage guards with private mempools with deadline enforcement with monitoring. The protocols that survive are the ones that assume every transaction is under attack.
About the Author
I am Uvin Vindula, a Web3 engineer and security auditor based between Sri Lanka and the UK. I audit DeFi protocols for front-running vulnerabilities, access control flaws, oracle manipulation, and economic exploits. Every audit I deliver includes a dedicated MEV analysis section — because a protocol that is functionally correct but MEV-extractable is still broken for its users.
If you are building a DeFi protocol and need a security audit that covers front-running, sandwich attacks, and MEV exposure, get in touch about my audit services. I will find the vulnerabilities before the bots do.
Working on a Web3 or AI project?

Uvin Vindula
Web3 and AI engineer based in Sri Lanka and the UK. Author of The Rise of Bitcoin. Director of Blockchain and Software Solutions at Terra Labz. Founder of uvin.lk — Sri Lanka's Bitcoin education platform with 10,000+ learners.