MEV Bot Crypto Detection: Protect Trades Like a Pro
Learn how MEV bots exploit blockchain transactions, detect sandwich attacks using Python, and protect your DeFi trades from front-running on major exchanges.
Learn how MEV bots exploit blockchain transactions, detect sandwich attacks using Python, and protect your DeFi trades from front-running on major exchanges.
Every time you submit a swap on Uniswap or any DEX, your transaction sits in the public mempool for a few seconds before miners or validators pick it up. In those seconds, automated programs called MEV bots are scanning it, calculating if they can profit from your trade, and if the numbers work — they will. They front-run you, sandwich you, and walk away with the difference. This is Maximal Extractable Value, and it costs DeFi traders hundreds of millions of dollars a year. The good news is that MEV bot activity leaves clear fingerprints on-chain, and with the right tools you can detect it, measure your exposure, and build defenses into your own trading stack.
MEV — Maximal Extractable Value — refers to profit that can be extracted from blockchain users by controlling the order of transactions within a block. MEV bots are automated programs that monitor the public mempool (the queue of unconfirmed transactions) and exploit predictable user behavior to generate profit. They are not hackers in the traditional sense. They are exploiting a structural feature of how public blockchains work: all pending transactions are visible to anyone before they confirm.
There are several categories of MEV bot strategies. Front-running bots spot your large buy order and submit an identical buy with higher gas, ensuring they execute first — pushing the price up before your trade fills. Sandwich bots wrap your transaction between a buy and a sell, profiting from the price movement your own trade causes. Arbitrage bots exploit price differences between DEXes — less harmful to individual traders, but they still extract value from the system. Liquidation bots race to be the first to liquidate undercollateralized positions on lending protocols like Aave and Compound.
MEV is not exclusive to Ethereum. It exists on any blockchain with a public mempool and competitive block production — including BNB Chain, Arbitrum, Optimism, and Polygon. If you are trading on DEXes, you are exposed.
Understanding the mechanics makes it easier to detect. Imagine you submit a swap: 5 ETH for USDC on Uniswap with 1% slippage tolerance. Your transaction hits the mempool. A MEV bot detects it within milliseconds. The bot calculates: if it buys ETH first, the price will move slightly, your trade will execute at a worse rate (within your 1% tolerance), and then the bot can sell ETH back at a profit. The bot submits two transactions — one before yours (higher gas) and one after (same block). You get the trade done, but at the worst price your slippage allowed. The bot pockets the difference.
On a busy day on Ethereum, sandwich attacks can consume 40-60% of your slippage budget on popular pairs. Traders using platforms like Binance or Bybit for centralized order books are not vulnerable to this — it is a DeFi-specific problem. But traders who combine CEX signals with DEX execution, a common strategy, are exposed every time they touch a liquidity pool.
The cleanest way to detect sandwich attacks is to analyze block data after the fact. If your transaction was sandwiched, you will see: a transaction from address X immediately before yours in the same block, and another transaction from address X immediately after yours. Both transactions touch the same token pair you traded. This is the signature. Here is a Python script using web3.py that checks whether a given transaction was sandwiched:
from web3 import Web3
# Connect to Ethereum node
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_INFURA_KEY'))
def detect_sandwich(tx_hash: str) -> dict:
"""Check if a transaction was sandwiched by an MEV bot."""
receipt = w3.eth.get_transaction_receipt(tx_hash)
tx = w3.eth.get_transaction(tx_hash)
block = w3.eth.get_block(receipt['blockNumber'], full_transactions=True)
tx_index = receipt['transactionIndex']
txs = block['transactions']
result = {
'sandwich_detected': False,
'victim_tx': tx_hash,
'front_run_tx': None,
'back_run_tx': None,
'attacker_address': None
}
# Need transactions on both sides
if tx_index == 0 or tx_index >= len(txs) - 1:
return result
prev_tx = txs[tx_index - 1]
next_tx = txs[tx_index + 1]
# Classic sandwich: same attacker before and after, different from victim
if (
prev_tx['from'].lower() == next_tx['from'].lower()
and prev_tx['from'].lower() != tx['from'].lower()
):
result['sandwich_detected'] = True
result['front_run_tx'] = prev_tx['hash'].hex()
result['back_run_tx'] = next_tx['hash'].hex()
result['attacker_address'] = prev_tx['from']
return result
# Usage
result = detect_sandwich('0xYOUR_TRANSACTION_HASH_HERE')
if result['sandwich_detected']:
print(f"Sandwiched by: {result['attacker_address']}")
print(f"Front-run tx: {result['front_run_tx']}")
print(f"Back-run tx: {result['back_run_tx']}")
else:
print('No sandwich detected.')
This script is a solid starting point. In practice, sophisticated MEV bots use proxy contracts and indirect routing to obscure their activity, so address matching alone will not catch everything. For deeper analysis, you can decode the transaction input data and compare which token contracts each transaction interacts with, flagging cases where adjacent transactions share the same pair even from different addresses.
Post-trade detection tells you when you got hit. Real-time mempool monitoring tells you when an attack is being set up — useful if you are running a trading bot that can react, or if you are building an alerting system that feeds into your signal workflow. Platforms like VoiceOfChain provide real-time market signals, and pairing those signals with mempool-level awareness gives you a significant edge when deciding when and how to execute.
import asyncio
from web3 import AsyncWeb3, WebSocketProvider
# Known high-risk method IDs (Uniswap V2/V3 swap signatures)
SWAP_METHODS = {
'0x38ed1739': 'swapExactTokensForTokens',
'0x7ff36ab5': 'swapExactETHForTokens',
'0x414bf389': 'exactInputSingle', # Uniswap V3
}
# Threshold: gas price > 2x base fee suggests priority insertion
MEV_GAS_MULTIPLIER = 2.0
async def monitor_mempool():
async with AsyncWeb3(WebSocketProvider(
'wss://mainnet.infura.io/ws/v3/YOUR_KEY'
)) as w3:
base_fee = (await w3.eth.get_block('latest'))['baseFeePerGas']
print(f'Base fee: {w3.from_wei(base_fee, "gwei"):.2f} Gwei')
print('Watching mempool for MEV bot activity...\n')
sub_id = await w3.eth.subscribe('newPendingTransactions')
async for tx_hash in w3.socket.process_subscriptions():
try:
tx = await w3.eth.get_transaction(tx_hash['result'])
if not tx or not tx.get('input') or len(tx['input']) < 10:
continue
method_id = tx['input'][:10]
if method_id not in SWAP_METHODS:
continue
gas_price = tx.get('maxFeePerGas') or tx.get('gasPrice', 0)
if gas_price > base_fee * MEV_GAS_MULTIPLIER:
gwei = w3.from_wei(gas_price, 'gwei')
print(f'[MEV ALERT] {SWAP_METHODS[method_id]}')
print(f' From: {tx["from"]}')
print(f' Gas price: {gwei:.1f} Gwei ({gwei / w3.from_wei(base_fee, "gwei"):.1f}x base)')
print(f' Tx hash: {tx_hash["result"]}\n')
except Exception:
continue
asyncio.run(monitor_mempool())
Mempool monitoring requires a WebSocket connection to an archive or full node. Infura, Alchemy, and QuickNode all offer WebSocket endpoints. Free tiers are sufficient for testing, but production use will need a paid plan with rate limit headroom.
Detection is half the battle. The other half is structuring your trades to minimize MEV exposure in the first place. The main levers are: tight slippage tolerance (less room for sandwiching), private transaction routing via Flashbots or MEV Blocker, short deadlines so stale transactions cannot be replayed, and splitting large trades into smaller chunks to reduce price impact. Here is a practical class that wraps these protections:
import time
from web3 import Web3
from eth_account import Account
from eth_account.signers.local import LocalAccount
class AntiMEVTrader:
"""Wraps swap parameters with MEV-resistant defaults."""
# Flashbots RPC — transactions sent here skip the public mempool
FLASHBOTS_RPC = 'https://rpc.flashbots.net'
# MEV Blocker by CoW Protocol — another private mempool option
MEV_BLOCKER_RPC = 'https://rpc.mevblocker.io'
def __init__(self, private_key: str, use_private_mempool: bool = True):
rpc = self.FLASHBOTS_RPC if use_private_mempool else \
'https://mainnet.infura.io/v3/YOUR_KEY'
self.w3 = Web3(Web3.HTTPProvider(rpc))
self.account: LocalAccount = Account.from_key(private_key)
self.use_private = use_private_mempool
def build_swap_params(
self,
token_in: str,
token_out: str,
amount_in_wei: int,
slippage_pct: float = 0.3, # Tight: 0.3% vs typical 1%
deadline_seconds: int = 45 # Short window
) -> dict:
min_out = int(amount_in_wei * (1 - slippage_pct / 100))
deadline = int(time.time()) + deadline_seconds
nonce = self.w3.eth.get_transaction_count(self.account.address)
params = {
'from': self.account.address,
'nonce': nonce,
'amountIn': amount_in_wei,
'amountOutMinimum': min_out,
'tokenIn': Web3.to_checksum_address(token_in),
'tokenOut': Web3.to_checksum_address(token_out),
'deadline': deadline,
'recipient': self.account.address,
}
if self.use_private:
# Do NOT tip extra gas — private mempool needs no priority bribe
params['maxPriorityFeePerGas'] = Web3.to_wei(1, 'gwei')
print('[AntiMEV] Routing through Flashbots private mempool')
else:
print('[AntiMEV] Public mempool — apply max caution on slippage')
return params
def assess_mev_risk(self, amount_usd: float, pool_tvl_usd: float) -> str:
"""Estimate MEV risk based on trade size vs pool depth."""
impact_pct = (amount_usd / pool_tvl_usd) * 100
if impact_pct > 1.0:
return 'HIGH — consider splitting trade'
elif impact_pct > 0.3:
return 'MEDIUM — use private mempool'
return 'LOW — standard slippage acceptable'
# Example: trade worth $10k in a $5M pool
trader = AntiMEVTrader(private_key='YOUR_KEY', use_private_mempool=True)
risk = trader.assess_mev_risk(amount_usd=10_000, pool_tvl_usd=5_000_000)
print(f'MEV Risk: {risk}')
params = trader.build_swap_params(
token_in='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', # WETH
token_out='0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', # USDC
amount_in_wei=int(1e18),
slippage_pct=0.3
)
print(params)
The private mempool approach via Flashbots is the most effective single change you can make. Transactions routed through Flashbots are never published to the public mempool — MEV bots scanning Ethereum's public pending transaction pool simply never see your trade. Coinbase's Base chain and other L2s have their own sequencer-based equivalents. On BNB Chain, platforms like Bybit and OKX have proprietary routing that achieves similar results for on-chain settlement of spot positions.
Beyond code, there are workflow-level habits that reduce MEV exposure significantly. First, trade in less congested windows — MEV bot competition spikes during high volatility when mempool backlogs form. Second, prefer aggregators like 1inch or CowSwap: CoW Protocol's batch auction model is inherently MEV-resistant because trades are settled peer-to-peer when possible rather than through AMM liquidity. Third, for large positions, use a CEX like Binance or OKX for the bulk of the execution and only use DEXes for the portion that requires on-chain settlement.
| Method | MEV Protection | Complexity | Cost |
|---|---|---|---|
| Tight slippage (0.3%) | Medium | Low | None |
| Flashbots / MEV Blocker | High | Medium | None (no tip) |
| CowSwap / 1inch Fusion | High | Low | Slight price impact |
| Split large trades | Medium | Low | More gas |
| CEX execution (Binance, OKX) | Complete | Low | Taker fee |
For traders who run signal-based strategies — for instance, using VoiceOfChain alerts to time entries — the execution layer matters as much as the signal quality. A perfect entry signal wasted to a sandwich attack is the same as a bad signal. Pairing real-time on-chain signals with private mempool routing closes that gap.
MEV bots are a structural feature of public blockchains, not a bug that will be patched away overnight. As a trader, your job is not to stop MEV from existing — it is to stop MEV from eating your trades. The combination of post-trade detection scripts, real-time mempool monitoring, and private transaction routing gives you a practical defense stack that requires no special permissions and costs nothing to run. Add tight slippage discipline and use CEXes like Binance, OKX, or Bybit for the heavy lifting when possible. Pair execution improvements with quality entry signals — tools like VoiceOfChain give you the market context to know when and what to trade — and you close the loop from signal to protected on-chain execution.