If your smart contract handles value-sensitive operations — swaps, auctions, liquidations, or token launches — it's a frontrunning target. MEV bots monitor the public mempool, extract your transaction details, and exploit them before your transaction executes. Protection isn't optional; it's a design requirement.
Why Standard Transactions Are Vulnerable
Every pending Ethereum transaction sits in the public mempool, fully visible to anyone. MEV bots have millisecond-latency connections to nodes, sophisticated simulation engines, and direct builder relationships. If your transaction is profitable to frontrun, it will be.
Pattern 1: Commit-Reveal
Users first commit to an action (submitting a hash), then reveal it in a later block. Bots can't see the action during the commit phase.
// Phase 1: Commit (action hidden)
function commit(bytes32 commitment) external {
require(commitments[msg.sender] == bytes32(0), "Already committed");
commitments[msg.sender] = commitment;
commitBlocks[msg.sender] = block.number;
}
// Phase 2: Reveal (after 1+ blocks)
function reveal(uint256 amount, uint256 price, bytes32 salt) external {
require(block.number > commitBlocks[msg.sender], "Too early");
require(block.number <= commitBlocks[msg.sender] + 100, "Expired");
require(
keccak256(abi.encode(amount, price, salt)) == commitments[msg.sender],
"Invalid reveal"
);
delete commitments[msg.sender];
_executeOrder(msg.sender, amount, price);
}
Pattern 2: Batch Auctions
Instead of processing orders sequentially (exploitable), batch all orders submitted during a time window and settle them at a uniform clearing price. CoW Protocol and Gnosis Auction use this approach.
Pattern 3: Private Transaction Submission
- Flashbots Protect — Sends transactions directly to block builders, skipping the public mempool
- MEV Blocker — Multi-builder protection that searches for MEV and rebates it to users
- MEV Share — Users share a portion of their transaction's MEV in exchange for protection
Pattern 4: Slippage Protection
// Always enforce minimum output amounts
function swap(
address tokenIn, address tokenOut,
uint256 amountIn, uint256 minAmountOut // User sets this
) external {
uint256 amountOut = calculateSwap(tokenIn, tokenOut, amountIn);
require(amountOut >= minAmountOut, "Slippage exceeded");
// Even if frontrun, the worst case is bounded
}
Which Pattern to Use?
| Use Case | Recommended Pattern |
|---|---|
| DEX swaps | Slippage protection + private submission |
| Auctions / NFT mints | Commit-reveal or batch auction |
| Liquidations | Batch or Dutch auction |
| Token launches | Batch auction + anti-snipe |
| Governance votes | Commit-reveal + snapshot voting |
- ✅ Default to private transaction submission for all user-facing operations
- ✅ Implement commit-reveal for auctions and time-sensitive operations
- ✅ Always enforce slippage tolerance parameters
- ✅ Consider batch auctions for high-value operations
Frontrunning is a design problem, not a deployment problem. Audit your protocol's MEV exposure before you go live.