Master essential MEV tools including Flashbots, Tenderly, MEV-Boost, and other popular platforms for effective MEV extraction
By the end of this course, you will be able to:
Installing and configuring Flashbots for bundle submission
90 minBuilding automated monitoring and notification systems
95 min| Tool | Primary Use | Difficulty | Cost | Rating |
|---|---|---|---|---|
| Flashbots | Bundle submission | Medium | Free | |
| Tenderly | Simulation & debugging | Easy | Freemium | |
| MEV-Boost | Builder connections | Hard | Free | |
| Dune Analytics | Data analytics | Medium | Freemium |
Understanding MEV extraction fundamentals
Live debugging and testing interface
Professional dashboard interfaces
MEV infrastructure components
# Install flashbots SDK
pip install flashbots
# Install web3 for Ethereum interactions
pip install web3
# Install aiohttp for async operations
pip install aiohttp
from flashbots import flashbot
from web3 import Web3
import os
# Initialize Web3 connection
w3 = Web3(Web3.HTTPProvider('https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY'))
# Set up Flashbots provider
# Replace with your private key
private_key = os.getenv('PRIVATE_KEY') # Use environment variables for security
account = w3.eth.account.from_key(private_key)
# Flashbots uses a different endpoint
flashbots_provider = flashbot.FlashbotsProvider(
'https://relay.flashbots.net',
private_key
)
print(f"Connected to Flashbots: {flashbots_provider.is_available()}")
from flashbots.types import BundleParams
async def submit_bundle():
# Get latest block
latest_block = w3.eth.block_number
target_block = latest_block + 1
# Example arbitrage transaction
tx_data = {
'to': '0xContractAddress',
'value': 0,
'data': '0x...', # Your transaction data
'gas': 200000,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.get_transaction_count(account.address),
}
# Sign transaction
signed_tx = account.sign_transaction(tx_data)
# Create bundle
bundle = {
'jsonrpc': '2.0',
'id': 1,
'method': 'eth_sendBundle',
'params': [{
'txs': [signed_tx.rawTransaction.hex()],
'blockNumber': hex(target_block),
'minTimestamp': int(time.time()),
'maxTimestamp': int(time.time()) + 60, # 1 minute validity
'revertingTxHashes': []
}]
}
# Submit bundle
response = flashbots_provider.send_bundle(bundle)
print(f"Bundle submitted: {response}")
return response
# Submit bundle
bundle_response = await submit_bundle()
import tenderly
import json
# Configure Tenderly
tenderly_username = "your_username"
tenderly_project = "your_project"
tenderly_private_key = "your_private_key"
# Initialize Tenderly SDK
tenderly.api_key = tenderly_private_key
tenderly.user_config.username = tenderly_username
tenderly.user_config.project_name = tenderly_project
class TenderlySimulator:
def __init__(self):
self.simulations = []
async def simulate_transaction(self,
contract_address: str,
function_name: str,
function_args: dict,
from_address: str,
value: int = 0):
"""Simulate a transaction using Tenderly"""
# Prepare simulation request
simulation_request = {
'network_id': '1', # Ethereum mainnet
'from': from_address,
'to': contract_address,
'input': self._encode_function_call(function_name, function_args),
'value': hex(value),
'simulation_kind': 'full',
}
try:
# Submit simulation
simulation = await tenderly.simulate_transaction(simulation_request)
# Analyze results
result = {
'transaction_hash': simulation.id,
'status': simulation.status,
'gas_used': simulation.transaction_info.gas_used,
'gas_price': simulation.transaction_info.gas_price,
'total_gas_cost': simulation.transaction_info.total_gas_cost,
'tx_fee': simulation.transaction_info.fee,
'logs': simulation.transaction_info.logs,
'revert_reason': simulation.transaction_info.revert_reason if hasattr(simulation.transaction_info, 'revert_reason') else None
}
self.simulations.append(result)
return result
except Exception as e:
print(f"Simulation failed: {e}")
return None
def _encode_function_call(self, function_name: str, args: dict) -> str:
"""Encode function call data using web3.py ABI encoding"""
try:
from web3 import Web3
# Create a Web3 instance for encoding
w3 = Web3()
# This would typically use the contract ABI to encode the function call
# For demonstration purposes, here's how it would work with actual ABI:
# abi = [...] # Contract ABI
# contract = w3.eth.contract(address=contract_address, abi=abi)
# encoded_data = contract.encodeABI(
# function_name=function_name,
# args=list(args.values())
# )
# return encoded_data
# Placeholder for demonstration
return "0x" + "0" * 68
except Exception as e:
print(f"Encoding error: {e}")
return "0x" + "0" * 68
def analyze_simulations(self):
"""Analyze all simulations for patterns"""
if not self.simulations:
return None
total_gas = sum(sim['gas_used'] for sim in self.simulations if sim['status'] == 'success')
successful = sum(1 for sim in self.simulations if sim['status'] == 'success')
total_simulations = len(self.simulations)
return {
'total_simulations': total_simulations,
'success_rate': successful / total_simulations,
'average_gas_used': total_gas / successful if successful > 0 else 0,
'total_gas_cost': sum(sim.get('total_gas_cost', 0) for sim in self.simulations if sim['status'] == 'success')
}
# Usage Example
simulator = TenderlySimulator()
# Simulate arbitrage transaction
result = await simulator.simulate_transaction(
contract_address="0x1234567890123456789012345678901234567890",
function_name="swapExactTokensForTokens",
function_args={
"amountIn": "1000000000000000000", # 1 ETH
"amountOutMin": "2350000000000000000000", # 2350 USDC
"path": ["ETH", "USDC"],
"to": "0x...",
"deadline": 1640995200
},
from_address="0xYourAddress",
value=0
)
if result:
print(f"Simulation successful: {result['status']}")
print(f"Gas used: {result['gas_used']}")
print(f"Total cost: {result['total_gas_cost']}")
# Analyze all simulations
analysis = simulator.analyze_simulations()
print(f"Success rate: {analysis['success_rate']:.2%}")
print(f"Average gas: {analysis['average_gas_used']:,.0f}")
# Export results for review
with open('simulation_results.json', 'w') as f:
json.dump({
'simulations': simulator.simulations,
'analysis': analysis
}, f, indent=2)
import asyncio
import aiohttp
import smtplib
from email.mime.text import MimeText
import time
import json
from datetime import datetime
class MEVMonitor:
def __init__(self, config):
self.config = config
self.session = None
self.is_running = False
self.alerts_sent = []
async def start_monitoring(self):
"""Start the monitoring service"""
self.is_running = True
self.session = aiohttp.ClientSession()
# Start all monitoring tasks
tasks = [
self.monitor_arbitrage_opportunities(),
self.monitor_liquidations(),
self.monitor_gas_prices(),
self.monitor_block_times()
]
await asyncio.gather(*tasks)
async def monitor_arbitrage_opportunities(self):
"""Monitor for arbitrage opportunities"""
while self.is_running:
try:
# Fetch latest prices from major DEXs
prices = await self.get_dex_prices()
# Calculate arbitrage opportunities
opportunities = self.find_arbitrage_opportunities(prices)
# Process opportunities
for opp in opportunities:
if opp['profit_margin'] > self.config['min_profit_margin']:
await self.alert_arbitrage_opportunity(opp)
await asyncio.sleep(5) # Check every 5 seconds
except Exception as e:
print(f"Error monitoring arbitrage: {e}")
await asyncio.sleep(10)
async def monitor_liquidations(self):
"""Monitor for liquidatable positions"""
while self.is_running:
try:
# Get all lending protocol positions
positions = await self.get_lending_positions()
for protocol, protocol_positions in positions.items():
for position in protocol_positions:
if position['health_factor'] < 1.1: # Close to liquidation
await self.alert_liquidation_opportunity(position, protocol)
await asyncio.sleep(10) # Check every 10 seconds
except Exception as e:
print(f"Error monitoring liquidations: {e}")
await asyncio.sleep(15)
async def get_dex_prices(self):
"""Fetch current prices from major DEXs"""
# This is a simplified example - you'd need to implement actual API calls
return {
'ETH': {
'uniswap': 2350.50,
'sushiswap': 2352.75,
'curve': 2349.80
}
}
def find_arbitrage_opportunities(self, prices):
"""Find profitable arbitrage opportunities"""
opportunities = []
for token, exchanges in prices.items():
exchange_prices = list(exchanges.values())
min_price = min(exchange_prices)
max_price = max(exchange_prices)
if max_price - min_price > 5: # At least $5 difference
buy_exchange = min(exchanges, key=exchanges.get)
sell_exchange = max(exchanges, key=exchanges.get)
profit_margin = (max_price - min_price) / min_price
opportunities.append({
'token': token,
'buy_exchange': buy_exchange,
'sell_exchange': sell_exchange,
'buy_price': min_price,
'sell_price': max_price,
'profit_margin': profit_margin,
'potential_profit': max_price - min_price
})
return opportunities
async def send_alert(self, message: str, priority: str = 'medium'):
"""Send alert notification"""
if priority == 'high' and message not in self.alerts_sent:
# Send email
await self.send_email_alert(message)
# Send Discord webhook
await self.send_discord_alert(message)
# Send Telegram
await self.send_telegram_alert(message)
# Log the alert
self.alerts_sent.append(message)
timestamp = datetime.now().isoformat()
alert_log = {
'timestamp': timestamp,
'message': message,
'priority': priority
}
with open('alerts.json', 'a') as f:
f.write(json.dumps(alert_log) + '\n')
async def send_email_alert(self, message: str):
"""Send email notification"""
try:
msg = MimeText(f"MEV Alert: {message}")
msg['Subject'] = "MEV Opportunity Alert"
msg['From'] = self.config['email']['from']
msg['To'] = self.config['email']['to']
server = smtplib.SMTP(self.config['email']['smtp_server'],
self.config['email']['smtp_port'])
server.starttls()
server.login(self.config['email']['username'],
self.config['email']['password'])
server.send_message(msg)
server.quit()
print("Alert email sent")
except Exception as e:
print(f"Failed to send email: {e}")
async def send_discord_alert(self, message: str):
"""Send Discord webhook notification"""
try:
webhook_data = {
'content': f"🚨 MEV Alert: {message}"
}
async with self.session.post(
self.config['discord']['webhook_url'],
json=webhook_data
) as response:
if response.status == 204:
print("Discord alert sent")
else:
print(f"Discord alert failed: {response.status}")
except Exception as e:
print(f"Failed to send Discord alert: {e}")
async def send_telegram_alert(self, message: str):
"""Send Telegram notification"""
try:
bot_token = self.config['telegram']['bot_token']
chat_id = self.config['telegram']['chat_id']
telegram_url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
message_data = {
'chat_id': chat_id,
'text': f"🚨 MEV Alert: {message}",
'parse_mode': 'HTML'
}
async with self.session.post(telegram_url, json=message_data) as response:
if response.status == 200:
print("Telegram alert sent")
else:
print(f"Telegram alert failed: {response.status}")
except Exception as e:
print(f"Failed to send Telegram alert: {e}")
async def stop_monitoring(self):
"""Stop the monitoring service"""
self.is_running = False
if self.session:
await self.session.close()
# Configuration
config = {
'min_profit_margin': 0.01, # 1% minimum profit
'email': {
'smtp_server': 'smtp.gmail.com',
'smtp_port': 587,
'username': 'your_email@gmail.com',
'password': 'your_app_password',
'from': 'your_email@gmail.com',
'to': 'recipient@gmail.com'
},
'discord': {
'webhook_url': 'https://discord.com/api/webhooks/...'
},
'telegram': {
'bot_token': 'your_bot_token',
'chat_id': 'your_chat_id'
}
}
# Usage
async def main():
monitor = MEVMonitor(config)
try:
await monitor.start_monitoring()
except KeyboardInterrupt:
await monitor.stop_monitoring()
if __name__ == "__main__":
asyncio.run(main())