| name | defi-mev-battletest |
| description | Expert knowledge for DeFi/MEV bot development including critical pitfalls, backtesting realities, AMM mechanics, MEV extraction strategies, and production failure modes |
DeFi/MEV Battle-Tested Expert Skill
MANDATORY CONSULTATION: This skill MUST be consulted for ANY DeFi bot development, MEV strategy implementation, or automated trading system. Real-world failures and lessons learned here prevent catastrophic losses.
Trigger Keywords
- arbitrage, MEV, searcher, bot, automated trading
- backtest, simulation, paper trading
- flash loan, sandwich, frontrun
- slippage, price impact, execution
- reorg, race condition, mempool
- market making, liquidity provision
1. CRITICAL PITFALL #1: "Arbitrage is Risk-Free" MYTH
Reality: Theoretical 0% risk, practical tail risk = DEATH
Hidden Risks in "Risk-Free" Arbitrage:
❌ Execution Risk
- Transaction reverts after gas spent
- Partial fills leave you with unwanted inventory
- Contract bugs in target protocols
❌ Reorg Risk (CRITICAL)
- Your profitable tx can be uncle'd
- 1-2 block reorgs happen DAILY on Ethereum
- Your "profit" disappears, gas cost remains
❌ Gas Spike Risk
- Base fee can 10x mid-execution
- Priority fee auctions drain profits
- Failed tx still costs full gas
❌ Latency Risk
- Block already mined before your tx lands
- State changed between simulation and execution
- Other searchers front-ran you
Real Numbers:
const theoreticalProfit = 0.05;
const executionCosts = {
gasOnSuccess: 0.01,
failureRate: 0.30,
gasOnFailure: 0.01,
reorgRate: 0.02,
slippageSlip: 0.005,
};
2. CRITICAL PITFALL #2: Backtest Overconfidence
80% of bots that fail in production looked great in backtests
Why Backtests Lie:
❌ Historical State ≠ Future Block State
- You're simulating against KNOWN state
- Live: state changes between blocks
- Mempool competition invisible in historical data
❌ Gas & Latency are Ex-Post Unknowable
- You backtest with actual gas prices
- Live: you must PREDICT gas prices
- Priority fee auctions are adversarial games
❌ Survivorship Bias
- You only see successful historical arbitrages
- Failed attempts not recorded on-chain
- "Found" opportunities may have been contested
❌ Market Impact Ignored
- Your own txs change the market
- Liquidity dries up when you need it most
- Large trades move price against you
Correct Approach:
async function badBacktest(historicalData) {
for (const block of historicalData) {
const profit = simulateWithPerfectState(block);
totalProfit += profit;
}
return totalProfit;
}
async function realisticTest(pendingBlock) {
const simResult = await simulateOnPendingState(pendingBlock);
const adjustedProfit = simResult.profit
* 0.70
* 0.80
- estimatedGas * 1.30;
return adjustedProfit;
}
3. CRITICAL PITFALL #3: AMM ≠ Order Book
Wrong slippage model = silent bleeding
Uniswap V3 Specific Gotchas:
interface V3Reality {
linearSlippage: false,
tickCrossing: 'each tick = separate fee payment',
liquidityGaps: 'can skip ticks with 0 liquidity',
concentratedLiquidity: 'most liquidity in narrow range',
}
const feeTiers = {
'0.01%': 'stablecoins only, ultra-tight spread',
'0.05%': 'correlated pairs (ETH/stETH)',
'0.30%': 'most pairs, default choice',
'1.00%': 'exotic pairs, low liquidity',
};
Curve-Specific Gotchas:
interface CurveReality {
amplificationFactor: number,
depegRisk: 'curve pools can trap you during depegs',
}
4. CRITICAL PITFALL #4: MEV Underestimation
Public mempool = free alpha donation
The MEV Food Chain:
Your transaction → Public Mempool → Searchers see it
↓
Sandwich Attack (you're the meat)
↓
Your "profit" becomes their profit
Private Orderflow is TABLE STAKES:
const submissionMethods = {
publicRPC: 'eth_sendRawTransaction',
flashbotsProtect: 'protect.flashbots.net',
mevBlocker: 'rpc.mevblocker.io',
flashbotsBundle: 'relay.flashbots.net',
mevShare: 'share MEV with users',
builderAPI: 'direct to block builders',
};
MEV-Share Reality:
interface MEVShareEconomics {
userShare: '50-90% of MEV',
searcherShare: '10-50% of MEV',
}
5. MUST-READ RESOURCES (10 articles = 1 year experience)
Tier 1: Foundational (READ FIRST)
📚 Paradigm Research
- "Liquidity Book" - AMM math from first principles
- "MEV... Wat Do?" - MEV taxonomy
- Every post on research.paradigm.xyz
📚 Flashbots Docs
- "MEV-Share" - orderflow auction design
- "Searching Post-Merge" - new MEV landscape
- docs.flashbots.net (entire site)
Tier 2: Practical Failures (LEARN FROM OTHERS' LOSSES)
Search Twitter/X for:
- "post-mortem"
- "we lost money because"
- "unexpected behavior"
- "exploit" + protocol name
Real lessons come from lost money.
Tier 3: Code Study (Skip star count, check content)
GitHub search for:
- MEV searcher bots (with reorg handling)
- Uniswap V3 math libraries
- Bundle simulation code
README keywords that indicate quality:
✅ "reorg handling"
✅ "race condition"
✅ "bundle simulation"
✅ "private mempool"
❌ "simple arbitrage"
❌ "guaranteed profit"
❌ "no risk"
Tier 4: Follow These Accounts
@bertcmiller - MEV searcher, practical insights
@hasufl - DeFi economics, mechanism design
@samczsun - Security, exploits, real failures
@0xfoobar - Technical MEV, searcher perspective
@barnabe_monnot - PBS, MEV-Boost internals
6. ARCHITECTURE PRINCIPLES (Non-Negotiable)
Separation of Concerns:
class Architecture {
strategyEngine: {
findOpportunities(): Opportunity[],
evaluateRisk(): RiskAssessment,
calculateSize(): PositionSize,
};
executionEngine: {
buildTransaction(): Transaction,
simulateBundle(): SimResult,
submitPrivate(): TxHash,
handleReorg(): void,
};
riskEngine: {
killSwitch(): void,
capitalAtRiskLimit(): USD,
maxLossPerHour(): USD,
maxConsecutiveLosses(): number,
};
}
Kill Switch Requirements:
interface KillSwitchConfig {
maxDrawdown: '5% of capital',
maxHourlyLoss: '$100',
maxDailyLoss: '$500',
consecutiveLosses: 5,
gasSpike: '10x normal',
emergencyStop: 'hardware button or separate process',
onKill: 'log state, close positions, notify',
}
7. SIMULATION-FIRST DEVELOPMENT
Not Paper Trading - Block Simulation:
interface SimulationApproach {
testAMMFormulas(): void,
testSlippageCalc(): void,
forkMainnet(): LocalFork,
simulateTrade(fork): SimResult,
getPendingBlock(): Block,
simulateInPending(): SimResult,
buildBundle(): Bundle,
simulateBundle(): BundleSimResult,
assumeCompetitors(): number,
simulateAuction(): AuctionResult,
}
Foundry/Anvil Fork Testing:
anvil --fork-url $ETH_RPC --fork-block-number 18500000
forge script SimulateArb --rpc-url http://localhost:8545
8. REAL FAILURE MODES (From Production)
Failure Mode 1: State Staleness
const maxStateAge = 1;
const stateCheck = async () => {
const currentBlock = await getBlockNumber();
if (currentBlock > simulationBlock + maxStateAge) {
return ABORT;
}
};
Failure Mode 2: Sandwich Bait
const isBait = (opportunity) => {
return suspiciousScore > THRESHOLD;
};
Failure Mode 3: Gas Price Prediction
const dynamicGas = async () => {
const pending = await getPendingBlock();
const competitorBids = analyzeCompetitorGas(pending);
const minViableBid = percentile(competitorBids, 80);
if (minViableBid > profitableThreshold) {
return SKIP;
}
return minViableBid * 1.1;
};
Failure Mode 4: Partial Execution
const atomicExecution = {
useFlashLoan: true,
checkInvariant: 'finalBalance >= initialBalance + minProfit',
};
9. CHECKLIST BEFORE GOING LIVE
□ Kill switch implemented and tested
□ Capital-at-risk limits set
□ Private mempool submission configured
□ Reorg handling implemented
□ State staleness checks added
□ Gas price prediction tested
□ Failure rate factored into expected value
□ Simulation matches production (within 20%)
□ Logs capture ALL failure modes
□ Alert system for anomalies
□ Manual emergency stop accessible
□ Tested with real money (small amount) for 1 week
10. EXPECTED VALUE CALCULATION (Realistic)
function realExpectedValue(opportunity: Opportunity): number {
const {
grossProfit,
gasOnSuccess,
failureRate,
gasOnFailure,
reorgRate,
competitionRate,
baitRate,
} = analyzeOpportunity(opportunity);
const successProfit = grossProfit - gasOnSuccess;
const successProb = (1 - failureRate) * (1 - reorgRate) * (1 - competitionRate) * (1 - baitRate);
const failureCost = gasOnFailure;
const failureProb = failureRate;
const reorgCost = gasOnSuccess;
const reorgProb = reorgRate * (1 - failureRate);
const EV = (successProb * successProfit)
- (failureProb * failureCost)
- (reorgProb * reorgCost);
return EV;
}
11. EMBEDDED KNOWLEDGE: MEV-Share Technical Deep Dive
This knowledge is embedded - no need to fetch external docs.
How MEV-Share Actually Works
interface MEVShareHints {
logs?: Log[],
calldata?: string,
contractAddress?: Address,
functionSelector?: string,
fullCalldata: 'HIDDEN',
value: 'HIDDEN',
from: 'HIDDEN',
}
const mevShareStrategy = {
backrunning: true,
sandwiching: 'limited',
};
MEV-Share Client Implementation
import { MevShareClient } from '@flashbots/mev-share-client';
const mevShareClient = new MevShareClient({
authSigner: wallet,
networkConfig: {
streamUrl: 'https://mev-share.flashbots.net',
bundleSubmitUrl: 'https://relay.flashbots.net',
},
});
mevShareClient.on('transaction', async (tx) => {
const backrunTx = await buildBackrun(tx);
await mevShareClient.sendBundle({
inclusion: { block: currentBlock + 1 },
body: [
{ hash: tx.hash },
{ tx: backrunTx },
],
privacy: { hints: ['calldata', 'logs'] },
});
});
12. EMBEDDED KNOWLEDGE: AMM Price Impact Mathematics
Constant Product Formula (Uniswap V2 style):
function calculatePriceImpact(
tradeSize: bigint,
reserveIn: bigint
): number {
return (2 * Number(tradeSize)) / Number(reserveIn);
}
function getAmountOut(
amountIn: bigint,
reserveIn: bigint,
reserveOut: bigint,
feeBps: number = 30
): bigint {
const amountInWithFee = amountIn * BigInt(10000 - feeBps);
const numerator = amountInWithFee * reserveOut;
denominator = reserveIn * + amountInWithFee;
numerator / denominator;
}
Slippage vs Price Impact (Common Confusion)
interface TradeExecution {
spotPrice: number,
expectedOutput: bigint,
maxSlippageBps: 50,
priceImpact: 'your trade moving the pool',
slippage: 'other trades moved pool since quote',
}
13. EMBEDDED KNOWLEDGE: Uniswap V3 Tick Mechanics
Why 1.0001? The Basis Point Standard:
const TICK_BASE = 1.0001;
function tickToPrice(tick: number): number {
return Math.pow(1.0001, tick);
}
function priceToTick(price: number): number {
return Math.floor(Math.log(price) / Math.log(1.0001));
}
Fee Tiers and Tick Spacing
const V3_FEE_TIERS = {
100: {
tickSpacing: 1,
useCase: 'Stablecoins (USDC/USDT)',
typicalSpread: '0.01-0.02%',
},
500: {
tickSpacing: 10,
useCase: 'Correlated pairs (ETH/stETH, WBTC/renBTC)',
typicalSpread: '0.05-0.10%',
},
3000: {
tickSpacing: 60,
useCase: 'Most pairs (ETH/USDC, etc)',
typicalSpread: '0.20-0.50%',
},
10000: {
tickSpacing: 200,
useCase: 'Exotic/low liquidity pairs',
typicalSpread: '0.50-2.00%',
},
};
Reading V3 Pool State
interface Slot0 {
sqrtPriceX96: bigint,
tick: number,
observationIndex: number,
observationCardinality: number,
observationCardinalityNext: number,
feeProtocol: number,
unlocked: boolean,
}
function sqrtPriceToPrice(
sqrtPriceX96: bigint,
decimals0: number,
decimals1: number
): number {
const Q96 = 2n ** 96n;
const price = (sqrtPriceX96 * sqrtPriceX96) / (Q96 * Q96);
const decimalAdjustment = 10 ** (decimals0 - decimals1);
return Number(price) * decimalAdjustment;
}
14. EMBEDDED KNOWLEDGE: Flashbots Bundle Submission
Bundle = Atomic sequence of transactions
import { FlashbotsBundleProvider } from '@flashbots/ethers-provider-bundle';
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider,
authSigner,
'https://relay.flashbots.net'
);
const bundle = [
{
signer: wallet,
transaction: {
to: targetContract,
data: calldata,
gasLimit: 500000,
maxFeePerGas: parseGwei('50'),
maxPriorityFeePerGas: parseGwei('3'),
type: 2,
},
},
];
const simulation = await flashbotsProvider.simulate(
bundle,
targetBlock
);
if (simulation.firstRevert) {
console.log('Bundle would revert:', simulation.firstRevert);
return;
}
const profit = simulation.results[0]. - simulation. * gasPrice;
(profit <= ) {
;
}
bundleSubmission = flashbotsProvider.(
bundle,
targetBlock
);
resolution = bundleSubmission.();
(resolution === .) {
.();
} (resolution === .) {
.();
} {
.();
}
Bundle Priority Fee Auction
const bundleWithCoinbasePayment = [
{
signer: wallet,
transaction: arbTx,
},
{
signer: wallet,
transaction: {
to: 'builder.coinbase',
value: parseEther('0.01'),
},
},
];
15. QUICK REFERENCE CHEAT SHEET
REMEMBER: The graveyard of DeFi bots is full of developers who thought they found an edge but didn't account for these realities. Read the post-mortems. Learn from others' losses. The market is adversarial - assume everyone is trying to extract value from you.