Flashbots RPC Tutorial: Stop Losing Trades to MEV Bots
Learn how to use Flashbots RPC to protect your transactions from front-running and MEV attacks — with Python code examples and real setup walkthroughs.
Learn how to use Flashbots RPC to protect your transactions from front-running and MEV attacks — with Python code examples and real setup walkthroughs.
If you've ever watched a trade get sandwiched — your buy goes through at a worse price while some bot profits in the same block — you've already met MEV (Maximal Extractable Value). Flashbots was built specifically to fight this. It gives you a private RPC endpoint that bypasses the public mempool entirely, so bots can't see your transaction before it lands on-chain. This tutorial walks you through setting it up, submitting bundles, and integrating it into your trading bot — whether you're trading DeFi on your own or using signals from platforms like VoiceOfChain to time entries.
Normally, when you send a transaction, it lands in the public mempool — a waiting room visible to everyone, including MEV searcher bots. These bots detect profitable trades and either front-run them (jumping in front of your buy to push up the price) or sandwich them (buying before and selling after). On high-volume DEXs, this can cost you anywhere from 0.5% to several percent per trade.
Flashbots provides a private relay: instead of broadcasting to the mempool, you send your transaction directly to Ethereum validators who've opted into the Flashbots system. Your transaction stays private until it's included in a block. No front-running, no sandwiching. For algorithmic traders routing significant size through Uniswap or Curve, this isn't optional — it's table stakes.
Flashbots RPC works on Ethereum mainnet. If you're trading on Binance Smart Chain or other EVM chains, look into chain-specific MEV protection like BNB Chain's private mempool solutions or Bybit's native order routing.
The simplest way to get MEV protection is to just change your RPC endpoint. Flashbots exposes a public RPC at https://rpc.flashbots.net that automatically routes transactions privately. Drop it into your wallet or bot config and you're already protected from the most common sandwich attacks.
from web3 import Web3
# Replace your standard Infura/Alchemy RPC with Flashbots
FLASHBOTS_RPC = "https://rpc.flashbots.net"
w3 = Web3(Web3.HTTPProvider(FLASHBOTS_RPC))
# Verify connection
print(f"Connected: {w3.is_connected()}")
print(f"Latest block: {w3.eth.block_number}")
# Your transactions now go through the private Flashbots relay
# Standard send_transaction works — no code changes needed
tx = {
'from': '0xYourAddress',
'to': '0xContractAddress',
'value': w3.to_wei(0.01, 'ether'),
'gas': 21000,
'maxFeePerGas': w3.to_wei(20, 'gwei'),
'maxPriorityFeePerGas': w3.to_wei(2, 'gwei'),
'nonce': w3.eth.get_transaction_count('0xYourAddress'),
'chainId': 1
}
signed = w3.eth.account.sign_transaction(tx, private_key='0xYourPrivateKey')
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
print(f"TX sent privately: {tx_hash.hex()}")
This is the zero-friction approach. You get private mempool routing without any bundle logic. It's enough for most DeFi traders executing through a script. The more powerful feature — bundles — is covered next.
A Flashbots bundle is a group of transactions that either all land in the same block or none of them do. This is the real power feature. You can execute a DeFi strategy that requires approve + swap + liquidity-add as one atomic unit, guaranteed in sequence. If any single transaction in the bundle would fail (say, slippage moved too far), the entire bundle is dropped — you don't burn gas on a partial execution.
To submit bundles, you need the flashbots-py library and a signing key (separate from your trading wallet) to authenticate with the Flashbots relay. The relay uses this signature to track your reputation — bundles that consistently land build trust and get prioritized.
import os
from eth_account import Account
from flashbots import flashbot
from web3 import Web3
# Setup
w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/YOUR_INFURA_KEY"))
# Flashbots signer — a separate key just for signing bundle requests
# This is NOT your trading wallet, just an identity key
FLASHBOTS_SIGNER = Account.from_key(os.environ["FLASHBOTS_SIGNING_KEY"])
# Inject Flashbots middleware
flashbot(w3, FLASHBOTS_SIGNER, "https://relay.flashbots.net")
TRADING_WALLET = Account.from_key(os.environ["TRADING_PRIVATE_KEY"])
def build_bundle(target_block: int) -> list:
"""Build a two-tx bundle: approve + swap"""
nonce = w3.eth.get_transaction_count(TRADING_WALLET.address)
base_fee = w3.eth.get_block('latest')['baseFeePerGas']
tx1_approve = {
'to': '0xTokenAddress',
'data': '0x...', # approve() calldata
'gas': 50000,
'maxFeePerGas': base_fee * 2,
'maxPriorityFeePerGas': w3.to_wei(1, 'gwei'),
'nonce': nonce,
'chainId': 1,
'type': 2
}
tx2_swap = {
'to': '0xUniswapRouter',
'data': '0x...', # swapExactTokensForETH calldata
'gas': 200000,
'maxFeePerGas': base_fee * 2,
'maxPriorityFeePerGas': w3.to_wei(1, 'gwei'),
'nonce': nonce + 1,
'chainId': 1,
'type': 2
}
signed_txs = [
TRADING_WALLET.sign_transaction(tx1_approve),
TRADING_WALLET.sign_transaction(tx2_swap)
]
return [{'signed_transaction': tx.rawTransaction} for tx in signed_txs]
# Target the next 3 blocks for inclusion
target = w3.eth.block_number + 1
bundle = build_bundle(target)
for block_num in range(target, target + 3):
result = w3.flashbots.send_bundle(bundle, target_block_number=block_num)
print(f"Bundle sent for block {block_num}: {result}")
Always target 3-5 consecutive blocks when submitting bundles. Network conditions vary, and a bundle that misses block N often lands on N+1 or N+2. If it hasn't landed after 5 blocks, re-evaluate your gas pricing.
Before spending real gas, simulate your bundle. Flashbots exposes a callBundle method that runs your transactions against a forked state and returns exactly what would happen — including gas used, revert reasons, and ETH balance changes. This is non-negotiable for production bots.
def simulate_bundle(bundle: list, block_number: int) -> dict:
"""
Simulate bundle execution before submitting.
Returns simulation result with gas estimates and state changes.
"""
try:
sim_result = w3.flashbots.simulate(
bundle,
block_tag=block_number
)
total_gas = sum(r['gasUsed'] for r in sim_result['results'])
total_value = sum(r.get('value', 0) for r in sim_result['results'])
print(f"Simulation passed — Gas: {total_gas}, Block: {block_number}")
for i, result in enumerate(sim_result['results']):
if result.get('revert'):
print(f"TX {i} would revert: {result['revert']}")
return None
return sim_result
except Exception as e:
print(f"Simulation failed: {e}")
return None
# Use before sending
target_block = w3.eth.block_number + 1
bundle = build_bundle(target_block)
sim = simulate_bundle(bundle, target_block)
if sim:
# Only submit if simulation passed
w3.flashbots.send_bundle(bundle, target_block_number=target_block)
else:
print("Bundle simulation failed — not submitting")
The simulate step catches the most common failure mode: stale calldata. If market conditions shifted since you built the bundle — say, the price on Uniswap moved and your minAmountOut is no longer satisfiable — simulation will catch it before you waste gas.
Flashbots becomes especially valuable when you're trading on signals — where speed and execution certainty both matter. VoiceOfChain, for example, surfaces real-time order-flow imbalances and whale accumulation patterns on-chain. When a high-confidence signal fires, you want your execution to be both fast and protected. Public mempool execution on a signal spike is practically asking to get sandwiched.
The architecture for a signal-driven Flashbots bot looks like this: a signal consumer listens for events (from VoiceOfChain or your own indicators), builds a bundle when confidence is high enough, simulates it, and submits to the Flashbots relay targeting the next 3 blocks. Compared to using raw Infura on Binance's BSC or OKX's Web3 chain, this approach gives you meaningful edge on Ethereum-native DeFi.
| Feature | Standard RPC (Infura/Alchemy) | Flashbots RPC |
|---|---|---|
| Mempool visibility | Public — bots see your TX | Private — invisible until mined |
| Sandwich protection | None | Full |
| Atomic multi-TX | No | Yes (bundles) |
| Gas refund on revert | No — gas spent | Yes — bundle drops silently |
| Inclusion guarantee | First come first served | Bid-based, targeted blocks |
| Setup complexity | Zero | Low-moderate |
Worth noting: Flashbots is Ethereum-specific. If your strategy runs on Bybit's on-chain products, OKX DEX, or Gate.io's chain, you'll need their respective MEV protection layers. But for mainnet Ethereum DeFi — Uniswap, Curve, Aave — Flashbots is the standard.
Flashbots RPC is one of those tools that feels like a minor infrastructure change but has a real impact on execution quality. Swapping your RPC endpoint takes five minutes and immediately removes you from the public mempool. Adding bundle logic takes an afternoon and unlocks atomic execution that simply isn't possible any other way.
For traders running size through Ethereum DeFi — whether acting on VoiceOfChain signals, running arbitrage between DEXs, or executing multi-step yield strategies — MEV protection isn't theoretical. It's the difference between your strategy's expected return and what you actually net after bots extract their cut. Start with the simple RPC swap, verify your transactions are going through privately, then graduate to bundles as your strategy complexity grows.