defi-mev-battletest
Expert knowledge for DeFi/MEV bot development including critical pitfalls, backtesting realities, AMM mechanics, MEV extraction strategies, and production failure modes
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o defi-mev-battletest.zip https://jpskill.com/download/17436.zip && unzip -o defi-mev-battletest.zip && rm defi-mev-battletest.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17436.zip -OutFile "$d\defi-mev-battletest.zip"; Expand-Archive "$d\defi-mev-battletest.zip" -DestinationPath $d -Force; ri "$d\defi-mev-battletest.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
defi-mev-battletest.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
defi-mev-battletestフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
DeFi/MEV 実戦経験に基づく専門スキル
必須コンサルテーション: このスキルは、あらゆる DeFi ボットの開発、MEV 戦略の実装、または自動取引システムにおいて、必ずコンサルテーションを受ける必要があります。現実世界での失敗と教訓が、壊滅的な損失を防ぎます。
トリガーキーワード
- 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. 致命的な落とし穴 #1: 「アービトラージはリスクフリー」という誤解
現実: 理論上のリスク 0%、実際にはテールリスク = 死
「リスクフリー」アービトラージに潜むリスク:
❌ 実行リスク
- トランザクションがガス消費後にリバートする
- 部分的な約定により、不要な在庫が残る
- 対象プロトコルのコントラクトのバグ
❌ Reorg リスク (重大)
- あなたの利益の出る tx が uncle 化される可能性がある
- 1-2 ブロックの reorg は Ethereum で毎日発生する
- あなたの「利益」は消え、ガスコストは残る
❌ ガススパイクリスク
- 基本料金が実行中に 10 倍になる可能性がある
- 優先手数料オークションが利益を食いつぶす
- 失敗した tx でも全額ガス代がかかる
❌ レイテンシーリスク
- あなたの tx が着地する前にブロックがすでにマイニングされている
- シミュレーションと実行の間で状態が変化した
- 他の searcher に front-run された
実際の数値:
// バックテストで見られるもの:
const theoreticalProfit = 0.05; // 5% のクリーンな利益
// 実際に起こること:
const executionCosts = {
gasOnSuccess: 0.01, // 1% ガス
failureRate: 0.30, // 30% の tx が失敗
gasOnFailure: 0.01, // それでもガス代を支払う
reorgRate: 0.02, // 2% が reorg される
slippageSlip: 0.005, // 0.5% の予期せぬスリッページ
};
// 実際の期待値:
// 0.70 * (0.05 - 0.01) - 0.30 * 0.01 - 0.02 * 0.04 = 0.0238
// MEV 競争の前に理論上の利益の 47% が消える
2. 致命的な落とし穴 #2: バックテストへの過信
本番環境で失敗するボットの 80% は、バックテストでは素晴らしい結果を出していた
バックテストが嘘をつく理由:
❌ 過去の状態 ≠ 将来のブロックの状態
- 既知の状態に対してシミュレーションしている
- ライブ: ブロック間で状態が変化する
- Mempool の競争は過去のデータでは見えない
❌ ガスとレイテンシーは事後的にしか知り得ない
- 実際のガス価格でバックテストする
- ライブ: ガス価格を予測する必要がある
- 優先手数料オークションは敵対的なゲーム
❌ 生存者バイアス
- 成功した過去のアービトラージしか見えない
- 失敗した試みはオンチェーンに記録されない
- 「見つかった」機会は争われた可能性がある
❌ マーケットインパクトの無視
- あなた自身の tx が市場を変化させる
- 最も必要なときに流動性が枯渇する
- 大量の取引は価格を不利な方向に動かす
正しいアプローチ:
// BAD: 完璧な情報でバックテストする
async function badBacktest(historicalData) {
for (const block of historicalData) {
const profit = simulateWithPerfectState(block);
totalProfit += profit;
}
return totalProfit; // 嘘
}
// GOOD: 現実的な条件でブロックをシミュレーションする
async function realisticTest(pendingBlock) {
// 1. (確定した状態ではなく) 保留中の状態に対してシミュレーションする
// 2. 現実的なレイテンシー (50-200ms) を追加する
// 3. 30% の失敗率を想定する
// 4. 「機会」の 20% がおとりであると想定する
// 5. ガス価格の不確実性 (±30%) を追加する
const simResult = await simulateOnPendingState(pendingBlock);
const adjustedProfit = simResult.profit
* 0.70 // 成功率
* 0.80 // おとりではない
- estimatedGas * 1.30; // ガス価格の不確実性
return adjustedProfit;
}
3. 致命的な落とし穴 #3: AMM ≠ 板取引
間違ったスリッページモデル = 静かな出血
Uniswap V3 固有の注意点:
// V3 のティック流動性は均一ではない
// ティック間の流動性はゼロになる可能性がある!
interface V3Reality {
// あなたが期待すること:
linearSlippage: false,
// 実際に起こること:
tickCrossing: '各ティック = 個別の手数料支払い',
liquidityGaps: '流動性が 0 のティックをスキップできる',
concentratedLiquidity: 'ほとんどの流動性は狭い範囲に集中している',
// 価格インパクトは曲線ではなくステップ関数:
// 小規模な取引: 0.01% のインパクト
// 中規模な取引: 0.5% のインパクト (ティックをまたぐ)
// 大規模な取引: 5% のインパクト (複数のティックをまたぐ)
}
// 手数料階層の選択は非常に重要
const feeTiers = {
'0.01%': 'ステーブルコインのみ、非常に狭いスプレッド',
'0.05%': '相関性の高いペア (ETH/stETH)',
'0.30%': 'ほとんどのペア、デフォルトの選択',
'1.00%': 'エキゾチックなペア、低い流動性',
};
// 間違い: ステーブルコインのスワップに 0.3% のプールを使用する
// 必要以上に 6 倍の手数料を支払っている
// 間違い: 変動性の高いペアに 0.05% のプールを使用する
// プールが存在しないか、流動性がない
Curve 固有の注意点:
// Curve StableSwap は異なる計算式を使用する
// A-factor がカーブの形状を決定する
interface CurveReality {
amplificationFactor: number, // A = 100 が一般的
// 低い A: 定数積 (Uniswap V2) に近い
// 高い A: 定数和 (1:1 スワップ) に近い
// 価格インパクトはステーブルコインでははるかに低い
// しかし、デペッグ時にははるかに高い
depegRisk: 'curve プールはデペッグ中にあなたを閉じ込める可能性がある',
}
// USDC デペッグの例 (2023 年 3 月):
// 期待: USDC→DAI を 0.99 でスワップ
// 現実: プールが枯渇し、10% 以上のスリッページ
4. 致命的な落とし穴 #4: MEV の過小評価
公開 mempool = 無料のアルファ寄付
MEV フードチェーン:
あなたのトランザクション → 公開 Mempool → Searcher がそれを見る
↓
サンドイッチ攻撃 (あなたは肉)
↓
あなたの「利益」が彼らの利益になる
プライベート注文フローは最低限の条件:
// プライベート送信を使用していない場合、優位性はない
const submissionMethods = {
// 公開 (抽出される)
publicRPC: 'eth_sendRawTransaction', // アービトラージには絶対に使用しない
// プライベート (最低限)
flashbotsProtect: 'protect.flashbots.net', // 無料、基本
mevBlocker: 'rpc.mevblocker.io', // 無料、良好
// 競争的 (本格的な searcher 向け)
flashbotsBundle: 'relay.flashbots.net', // バンドル送信
mevShare: 'ユーザーと MEV を共有する', // 一部のフローに必要
// ビルダーダイレクト (高度)
builderAPI: 'ブロックビルダーに直接', // Lowes
(原文はここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
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:
// What you see in backtest:
const theoreticalProfit = 0.05; // 5% clean profit
// What actually happens:
const executionCosts = {
gasOnSuccess: 0.01, // 1% gas
failureRate: 0.30, // 30% of txs fail
gasOnFailure: 0.01, // Still pay gas
reorgRate: 0.02, // 2% get reorg'd
slippageSlip: 0.005, // 0.5% unexpected slippage
};
// Real expected value:
// 0.70 * (0.05 - 0.01) - 0.30 * 0.01 - 0.02 * 0.04 = 0.0238
// 47% of theoretical profit GONE before MEV competition
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:
// BAD: Backtest with perfect information
async function badBacktest(historicalData) {
for (const block of historicalData) {
const profit = simulateWithPerfectState(block);
totalProfit += profit;
}
return totalProfit; // LIES
}
// GOOD: Block simulation with realistic conditions
async function realisticTest(pendingBlock) {
// 1. Simulate against PENDING state (not confirmed)
// 2. Add realistic latency (50-200ms)
// 3. Assume 30% failure rate
// 4. Assume 20% of "opportunities" are bait
// 5. Add gas price uncertainty (±30%)
const simResult = await simulateOnPendingState(pendingBlock);
const adjustedProfit = simResult.profit
* 0.70 // success rate
* 0.80 // not bait
- estimatedGas * 1.30; // gas uncertainty
return adjustedProfit;
}
3. CRITICAL PITFALL #3: AMM ≠ Order Book
Wrong slippage model = silent bleeding
Uniswap V3 Specific Gotchas:
// V3 Tick Liquidity is NON-UNIFORM
// Liquidity can be ZERO between ticks!
interface V3Reality {
// What you expect:
linearSlippage: false,
// What actually happens:
tickCrossing: 'each tick = separate fee payment',
liquidityGaps: 'can skip ticks with 0 liquidity',
concentratedLiquidity: 'most liquidity in narrow range',
// Price impact is STEP FUNCTION not curve:
// Small trade: 0.01% impact
// Medium trade: 0.5% impact (crossed tick)
// Large trade: 5% impact (crossed multiple ticks)
}
// Fee Tier Selection MATTERS ENORMOUSLY
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',
};
// WRONG: Using 0.3% pool for stablecoin swap
// You're paying 6x more fees than necessary
// WRONG: Using 0.05% pool for volatile pair
// Pool doesn't exist or has no liquidity
Curve-Specific Gotchas:
// Curve StableSwap has DIFFERENT math
// A-factor determines curve shape
interface CurveReality {
amplificationFactor: number, // A = 100 typical
// Low A: more like constant-product (Uniswap V2)
// High A: more like constant-sum (1:1 swap)
// Price impact is MUCH LOWER for stables
// But MUCH HIGHER at depegs
depegRisk: 'curve pools can trap you during depegs',
}
// USDC depeg example (March 2023):
// Expected: swap USDC→DAI at 0.99
// Reality: pool drained, 10%+ slippage
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:
// If you're not using private submission, you have NO edge
const submissionMethods = {
// PUBLIC (you will be extracted)
publicRPC: 'eth_sendRawTransaction', // NEVER for arb
// PRIVATE (minimum viable)
flashbotsProtect: 'protect.flashbots.net', // Free, basic
mevBlocker: 'rpc.mevblocker.io', // Free, good
// COMPETITIVE (for serious searchers)
flashbotsBundle: 'relay.flashbots.net', // Bundle submission
mevShare: 'share MEV with users', // Required for some flow
// BUILDER DIRECT (advanced)
builderAPI: 'direct to block builders', // Lowest latency
};
MEV-Share Reality:
// New paradigm: users get kickbacks
// Searchers must share profits
interface MEVShareEconomics {
userShare: '50-90% of MEV',
searcherShare: '10-50% of MEV',
// This means:
// Your arbitrage opportunity is SMALLER
// Competition is HIGHER
// Only ultra-efficient searchers survive
}
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:
// CRITICAL: Execution engine SEPARATE from strategy
class Architecture {
// Strategy Layer (what to do)
strategyEngine: {
findOpportunities(): Opportunity[],
evaluateRisk(): RiskAssessment,
calculateSize(): PositionSize,
};
// Execution Layer (how to do it)
executionEngine: {
buildTransaction(): Transaction,
simulateBundle(): SimResult,
submitPrivate(): TxHash,
handleReorg(): void,
};
// Risk Layer (when to stop)
riskEngine: {
killSwitch(): void, // MUST EXIST
capitalAtRiskLimit(): USD, // MUST BE SET
maxLossPerHour(): USD, // CIRCUIT BREAKER
maxConsecutiveLosses(): number,
};
}
Kill Switch Requirements:
// NON-NEGOTIABLE: Every bot needs these
interface KillSwitchConfig {
// Automatic triggers
maxDrawdown: '5% of capital',
maxHourlyLoss: '$100',
maxDailyLoss: '$500',
consecutiveLosses: 5,
gasSpike: '10x normal',
// Manual override
emergencyStop: 'hardware button or separate process',
// State preservation
onKill: 'log state, close positions, notify',
}
// BAD: "I'll add kill switch later"
// GOOD: Kill switch is FIRST feature implemented
7. SIMULATION-FIRST DEVELOPMENT
Not Paper Trading - Block Simulation:
// Paper trading: fake orders against real market
// Block simulation: real orders against simulated state
interface SimulationApproach {
// Level 1: Unit test math
testAMMFormulas(): void,
testSlippageCalc(): void,
// Level 2: State fork simulation
forkMainnet(): LocalFork,
simulateTrade(fork): SimResult,
// Level 3: Pending block simulation
getPendingBlock(): Block,
simulateInPending(): SimResult,
// Level 4: Bundle simulation
buildBundle(): Bundle,
simulateBundle(): BundleSimResult,
// Level 5: Competition simulation
assumeCompetitors(): number,
simulateAuction(): AuctionResult,
}
Foundry/Anvil Fork Testing:
# Fork mainnet at specific block
anvil --fork-url $ETH_RPC --fork-block-number 18500000
# Run simulation
forge script SimulateArb --rpc-url http://localhost:8545
8. REAL FAILURE MODES (From Production)
Failure Mode 1: State Staleness
// You simulated against block N
// You submit to block N+1
// State changed → tx reverts → gas lost
// Solution:
const maxStateAge = 1; // blocks
const stateCheck = async () => {
const currentBlock = await getBlockNumber();
if (currentBlock > simulationBlock + maxStateAge) {
return ABORT; // Don't submit stale tx
}
};
Failure Mode 2: Sandwich Bait
// "Opportunity" placed by searcher
// You take it → get sandwiched → lose more than "profit"
// Solution:
const isBait = (opportunity) => {
// Check if opportunity appeared in mempool recently
// Check if liquidity is suspicious
// Check if profit is "too good"
return suspiciousScore > THRESHOLD;
};
Failure Mode 3: Gas Price Prediction
// You bid 10 gwei priority fee
// Block lands with 50 gwei minimum
// Your tx not included → opportunity gone
// Solution:
const dynamicGas = async () => {
const pending = await getPendingBlock();
const competitorBids = analyzeCompetitorGas(pending);
const minViableBid = percentile(competitorBids, 80);
if (minViableBid > profitableThreshold) {
return SKIP; // Not worth competing
}
return minViableBid * 1.1; // Slight overbid
};
Failure Mode 4: Partial Execution
// Multi-leg arb: leg 1 executes, leg 2 reverts
// You're stuck with unwanted tokens
// Solution:
const atomicExecution = {
// All legs in single transaction
useFlashLoan: true, // Revert entire tx if unprofitable
// Or: use smart contract that checks final state
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)
// The formula that actually matters:
function realExpectedValue(opportunity: Opportunity): number {
const {
grossProfit,
gasOnSuccess,
failureRate,
gasOnFailure,
reorgRate,
competitionRate,
baitRate,
} = analyzeOpportunity(opportunity);
// Success case
const successProfit = grossProfit - gasOnSuccess;
const successProb = (1 - failureRate) * (1 - reorgRate) * (1 - competitionRate) * (1 - baitRate);
// Failure cases
const failureCost = gasOnFailure;
const failureProb = failureRate;
const reorgCost = gasOnSuccess; // Already paid gas
const reorgProb = reorgRate * (1 - failureRate);
// Expected value
const EV = (successProb * successProfit)
- (failureProb * failureCost)
- (reorgProb * reorgCost);
// If EV < 0, DO NOT EXECUTE
// If EV < minThreshold, probably not worth the risk
return EV;
}
// Example with realistic numbers:
// Gross profit: $100
// Gas (success): $5
// Gas (failure): $5
// Failure rate: 30%
// Reorg rate: 2%
// Competition rate: 50%
// Bait rate: 5%
// Success prob: 0.70 * 0.98 * 0.50 * 0.95 = 0.326
// EV = 0.326 * $95 - 0.30 * $5 - 0.014 * $5 = $29.27
// Your "$100 opportunity" is actually worth ~$29
// And that's BEFORE accounting for your infrastructure costs
11. EMBEDDED KNOWLEDGE: MEV-Share Technical Deep Dive
This knowledge is embedded - no need to fetch external docs.
How MEV-Share Actually Works
// MEV-Share reveals HINTS, not full transactions
// This is the critical difference from public mempool
interface MEVShareHints {
// What you CAN see:
logs?: Log[], // Event logs (partial)
calldata?: string, // Function selector only (first 4 bytes)
contractAddress?: Address,
functionSelector?: string,
// What you CANNOT see:
fullCalldata: 'HIDDEN', // No parameters
value: 'HIDDEN', // No ETH amount
from: 'HIDDEN', // No sender address
}
// Strategy Shift Required:
// OLD (public mempool): See full tx → calculate exact sandwich
// NEW (MEV-Share): See hints → probabilistic backrun only
const mevShareStrategy = {
// What still works:
backrunning: true, // Wait for tx, backrun with your arb
// What's harder:
sandwiching: 'limited', // Can't calculate exact frontrun
// Key insight:
// You're bidding on PARTIAL information
// Must share profits with users (refund mechanism)
};
MEV-Share Client Implementation
import { MevShareClient } from '@flashbots/mev-share-client';
// Connect to MEV-Share SSE stream
const mevShareClient = new MevShareClient({
authSigner: wallet,
networkConfig: {
streamUrl: 'https://mev-share.flashbots.net',
bundleSubmitUrl: 'https://relay.flashbots.net',
},
});
// Listen for pending transactions (hints only)
mevShareClient.on('transaction', async (tx) => {
// tx.hash - the pending tx hash
// tx.logs - partial event logs
// tx.to - target contract
// tx.functionSelector - first 4 bytes of calldata
// You DON'T get: full calldata, from address, value
const backrunTx = await buildBackrun(tx);
// Bundle must include original tx hash
await mevShareClient.sendBundle({
inclusion: { block: currentBlock + 1 },
body: [
{ hash: tx.hash }, // Original tx (by hash reference)
{ tx: backrunTx }, // Your backrun
],
privacy: { hints: ['calldata', 'logs'] },
});
});
12. EMBEDDED KNOWLEDGE: AMM Price Impact Mathematics
Constant Product Formula (Uniswap V2 style):
// The fundamental invariant: x * y = k
// x = token0 reserves
// y = token1 reserves
// k = constant (increases with fees)
// Price Impact Formula (MEMORIZE THIS):
// For a trade of size Δx:
// Price Impact ≈ 2 * Δx / x
//
// Example: Trade 1 ETH in a pool with 100 ETH
// Impact ≈ 2 * 1 / 100 = 2%
function calculatePriceImpact(
tradeSize: bigint,
reserveIn: bigint
): number {
// Rule of thumb: impact = 2x your order relative to pool
return (2 * Number(tradeSize)) / Number(reserveIn);
}
// CRITICAL: Why 2x?
// Because you're moving the price FROM spot TO execution
// The average execution price is between start and end
// This creates ~2x the "naive" calculation
// Exact formula for amount out:
function getAmountOut(
amountIn: bigint,
reserveIn: bigint,
reserveOut: bigint,
feeBps: number = 30 // 0.3% = 30 bps
): bigint {
const amountInWithFee = amountIn * BigInt(10000 - feeBps);
const numerator = amountInWithFee * reserveOut;
const denominator = reserveIn * 10000n + amountInWithFee;
return numerator / denominator;
}
Slippage vs Price Impact (Common Confusion)
// PRICE IMPACT: Deterministic, based on your trade size
// SLIPPAGE: Non-deterministic, price moves between quote and execution
interface TradeExecution {
// At quote time:
spotPrice: number,
expectedOutput: bigint,
// Your settings:
maxSlippageBps: 50, // 0.5% allowed slippage
// At execution time:
priceImpact: 'your trade moving the pool',
slippage: 'other trades moved pool since quote',
// Total cost = priceImpact + slippage
// If total > maxSlippageBps → tx reverts
}
// BEST PRACTICE:
// 1. Estimate price impact from pool math
// 2. Add buffer for slippage (depends on volatility)
// 3. Never set slippage > 1% unless you KNOW why
// 4. Monitor for sandwich attacks if slippage is high
13. EMBEDDED KNOWLEDGE: Uniswap V3 Tick Mechanics
Why 1.0001? The Basis Point Standard:
// Each tick represents 1 basis point (0.01%) price change
// tick_spacing determines which ticks are usable
const TICK_BASE = 1.0001; // Price multiplier per tick
// Price at tick i:
// price(i) = 1.0001^i
// Example:
// tick 0: price = 1.0001^0 = 1.000
// tick 100: price = 1.0001^100 ≈ 1.0101 (1.01% higher)
// tick 1000: price = 1.0001^1000 ≈ 1.1052 (10.52% higher)
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
// CRITICAL: Tick spacing varies by fee tier
// This affects liquidity granularity
const V3_FEE_TIERS = {
100: { // 0.01% fee
tickSpacing: 1,
useCase: 'Stablecoins (USDC/USDT)',
typicalSpread: '0.01-0.02%',
},
500: { // 0.05% fee
tickSpacing: 10,
useCase: 'Correlated pairs (ETH/stETH, WBTC/renBTC)',
typicalSpread: '0.05-0.10%',
},
3000: { // 0.30% fee
tickSpacing: 60,
useCase: 'Most pairs (ETH/USDC, etc)',
typicalSpread: '0.20-0.50%',
},
10000: { // 1.00% fee
tickSpacing: 200,
useCase: 'Exotic/low liquidity pairs',
typicalSpread: '0.50-2.00%',
},
};
// Why this matters for MEV:
// 1. Concentrated liquidity means DISCONTINUOUS price impact
// 2. Crossing a tick boundary = paying fee on that tick's liquidity
// 3. Large trades can "blow through" low-liquidity ticks
Reading V3 Pool State
// The slot0 call gives you current state
interface Slot0 {
sqrtPriceX96: bigint, // sqrt(price) * 2^96
tick: number, // Current tick
observationIndex: number,
observationCardinality: number,
observationCardinalityNext: number,
feeProtocol: number,
unlocked: boolean,
}
// Convert sqrtPriceX96 to human-readable price:
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;
}
// Liquidity at specific ticks:
// Use ticks(tickIndex) to get liquidityNet
// liquidityNet = change in liquidity when crossing this tick
// Positive = liquidity added when price moves up
// Negative = liquidity removed when price moves up
14. EMBEDDED KNOWLEDGE: Flashbots Bundle Submission
Bundle = Atomic sequence of transactions
import { FlashbotsBundleProvider } from '@flashbots/ethers-provider-bundle';
// Bundle submission flow:
// 1. Build transactions
// 2. Simulate against pending state
// 3. Submit to Flashbots relay
// 4. Wait for inclusion or rejection
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider,
authSigner,
'https://relay.flashbots.net'
);
// Build bundle
const bundle = [
{
signer: wallet,
transaction: {
to: targetContract,
data: calldata,
gasLimit: 500000,
maxFeePerGas: parseGwei('50'),
maxPriorityFeePerGas: parseGwei('3'),
type: 2,
},
},
];
// Simulate BEFORE submitting
const simulation = await flashbotsProvider.simulate(
bundle,
targetBlock
);
if (simulation.firstRevert) {
console.log('Bundle would revert:', simulation.firstRevert);
return; // Don't submit failing bundle
}
// Calculate profitability
const profit = simulation.results[0].value - simulation.totalGasUsed * gasPrice;
if (profit <= 0) {
return; // Not profitable after gas
}
// Submit bundle
const bundleSubmission = await flashbotsProvider.sendBundle(
bundle,
targetBlock
);
// Wait for resolution
const resolution = await bundleSubmission.wait();
if (resolution === FlashbotsBundleResolution.BundleIncluded) {
console.log('Bundle included!');
} else if (resolution === FlashbotsBundleResolution.BlockPassedWithoutInclusion) {
console.log('Bundle not included - outbid or block full');
} else {
console.log('Bundle rejected by relay');
}
Bundle Priority Fee Auction
// Flashbots uses EFFECTIVE PRIORITY FEE for ordering
// effectiveGasPrice = min(maxFeePerGas, baseFee + maxPriorityFeePerGas)
// Coinbase transfer trick:
// Instead of high priority fee, pay builder directly
// This hides your bid from competitors
const bundleWithCoinbasePayment = [
// Your profitable transaction
{
signer: wallet,
transaction: arbTx,
},
// Pay the block builder
{
signer: wallet,
transaction: {
to: 'builder.coinbase', // Special: goes to block builder
value: parseEther('0.01'), // Your "bid"
},
},
];
// Why coinbase payment?
// 1. Priority fee is visible in mempool simulations
// 2. Competitors can see and outbid
// 3. Coinbase payment is private until block lands
15. QUICK REFERENCE CHEAT SHEET
// === PRICE IMPACT ===
// V2-style: impact ≈ 2 * tradeSize / poolReserve
// V3-style: depends on liquidity distribution, check each tick
// === MEV-SHARE ===
// You see: logs, function selector, target contract
// You don't see: calldata params, value, sender
// Strategy: backrun only, share profits
// === GAS ESTIMATION ===
// Simple swap: 100-150k gas
// V3 multi-hop: 200-400k gas
// Flash loan + arb: 400-800k gas
// Always add 20% buffer
// === TICK MATH ===
// price(tick) = 1.0001^tick
// tick(price) = log(price) / log(1.0001)
// Fee tier → tick spacing: 0.01%→1, 0.05%→10, 0.3%→60, 1%→200
// === BUNDLE SUBMISSION ===
// 1. Simulate first
// 2. Check profitability after gas
// 3. Use coinbase payment for competitive bids
// 4. Handle BlockPassedWithoutInclusion gracefully
// === RED FLAGS (ABORT) ===
// - Price impact > 1% on "small" trade
// - Opportunity profit < 2x gas cost
// - Unknown token without verification
// - Pool created < 24 hours ago
// - Single-sided liquidity (rug setup)
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.