MEV attack detection system development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
MEV attack detection system development
Complex
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1051
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    827
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    850

Development of MEV Attack Detection System

MEV (Maximal Extractable Value) is the value that validators and searchers extract by reordering transactions within a block. Technically, this isn't always "attack"—some MEV is harmless (arbitrage between DEXs restores price equilibrium). But sandwich attacks, liquidation front-running, and time-bandit attacks directly harm users and protocols. MEV detection system solves two tasks: post-hoc analysis (what happened to user) and proactive protection (preventing specific attack types).

Typology of MEV Attacks

Sandwich Attack

Classic and most common type. Scheme:

  1. Victim sends swap to DEX with large slippage tolerance
  2. Attacker sees transaction in mempool
  3. Inserts buy BEFORE victim (front-run), raising price
  4. Victim executes at higher price
  5. Attacker sells AFTER victim (back-run), locking profit

Block identifier: three consecutive transactions with characteristic pattern.

Liquidation Front-Running

Position on lending protocol (Aave, Compound) becomes liquidatable. Multiple bots see this and race for liquidation reward. First wins—others wasted gas. Not attack on user but creates waste and MEV race.

More aggressive version: attacker manipulates price oracle (via flash loan in pool used as oracle) to force position under liquidation.

Arbitrage MEV

Technically not attack: if ETH price on Uniswap v3 differs from Curve by 0.3%—arbitrage bot equalizes price, getting profit. This benefits market. Nevertheless, protocols need understanding arbitrage flows.

JIT (Just-in-Time) Liquidity

Specific to Uniswap v3. Attacker sees large swap in mempool, adds concentrated liquidity exactly in right range before swap, collects fees, removes liquidity after. Honest LP loses fees. Debatable form of MEV—technically allowed by protocol.

Time-Bandit Attack

Reorg attack: if MEV in few blocks ago exceeds cost of chain reorganization—attacking validator can try rewriting history. On Ethereum post-Merge extremely difficult and expensive, but remains theoretical threat for PoS chains with small stake.

On-Chain Sandwich Detection

Identification Algorithm

Sandwich—three transactions in one block with specific relationships:

from dataclasses import dataclass
from typing import Optional
import pandas as pd

@dataclass
class SwapEvent:
    tx_hash: str
    block_number: int
    tx_index: int  # position in block
    sender: str
    token_in: str
    token_out: str
    amount_in: int
    amount_out: int
    pool: str

def detect_sandwiches(swaps_in_block: list[SwapEvent]) -> list[dict]:
    sandwiches = []

    # Group by pool
    by_pool = {}
    for swap in swaps_in_block:
        by_pool.setdefault(swap.pool, []).append(swap)

    for pool, pool_swaps in by_pool.items():
        # Sort by position in block
        pool_swaps.sort(key=lambda s: s.tx_index)

        # Find pattern: A buys → victim buys → A sells (same token_in/out)
        for i, front in enumerate(pool_swaps[:-2]):
            victim = pool_swaps[i + 1]
            back = pool_swaps[i + 2]

            is_sandwich = (
                front.sender == back.sender and           # one attacker
                front.sender != victim.sender and          # victim different
                front.token_in == victim.token_in and      # same direction
                back.token_in == front.token_out and       # reversal
                back.token_out == front.token_in
            )

            if is_sandwich:
                # Calculate attacker profit
                attacker_profit = (
                    back.amount_out - front.amount_in
                )
                # Calculate victim damage: execution price difference
                victim_price = victim.amount_out / victim.amount_in
                # fair_price need to calculate without front-run transaction (simulation)

                sandwiches.append({
                    'front_tx': front.tx_hash,
                    'victim_tx': victim.tx_hash,
                    'back_tx': back.tx_hash,
                    'attacker': front.sender,
                    'pool': pool,
                    'attacker_profit_tokens': attacker_profit,
                    'block': front.block_number,
                })

    return sandwiches

Measuring Victim Damage

For accurate damage calculation, simulate how victim's transaction would execute without front-run. Requires archive node access and replay with state override:

async def simulate_swap_without_frontrun(
    victim_tx: dict,
    frontrun_tx: dict,
    block_number: int,
    rpc_url: str
) -> dict:
    """
    Simulates victim_tx on state BEFORE frontrun_tx
    """
    w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(rpc_url))

    # Get state at previous block
    prev_block_state = block_number - 1

    # eth_call with block_number = prev_block (without frontrun)
    result = await w3.eth.call(
        {
            'from': victim_tx['from'],
            'to': victim_tx['to'],
            'data': victim_tx['input'],
            'gas': victim_tx['gas'],
        },
        prev_block_state
    )

    return {'simulated_output': result}

Difference between actual amount_out and simulated—MEV extracted from victim.

Detection via Mempool Monitoring

Flashbots MEV-Share and MEV-Boost Data

Flashbots publishes MEV-Boost data via API: https://boost-relay.flashbots.net/relay/v1/data/bidtraces/proposer_payload_delivered. Historical data about blocks passed through MEV-Boost with MEV-bid information.

More detailed data—via Flashbots MEV-Share API: information about bundle content published post-execution.

Custom Mempool Observer

For real-time: connect to mempool via eth_subscribe("pendingTransactions"):

import { createPublicClient, webSocket, parseAbi, decodeEventLog } from 'viem';

const client = createPublicClient({
    transport: webSocket('wss://mainnet.infura.io/ws/v3/KEY'),
});

// Subscribe to pending transactions
const unwatch = client.watchPendingTransactions({
    onTransactions: async (hashes) => {
        for (const hash of hashes) {
            const tx = await client.getTransaction({ hash });
            if (tx && isLargeSwap(tx)) {
                // This is transaction with large slippage - possible victim
                await analyzeMevRisk(tx);
            }
        }
    },
});

Problem: most MEV bots now use private mempools (Flashbots bundles, MEV Blocker). Public mempool sees only partial picture.

Protective Mechanisms for Protocols

Commit-Reveal for Sensitive Operations

User first publishes transaction hash (commit), then reveal. Attacker doesn't know parameters until reveal:

contract CommitRevealSwap {
    mapping(bytes32 => uint256) public commits;
    uint256 public constant REVEAL_DELAY = 2; // blocks

    function commit(bytes32 commitment) external {
        commits[commitment] = block.number;
    }

    function reveal(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        bytes32 salt
    ) external {
        bytes32 commitment = keccak256(
            abi.encodePacked(msg.sender, tokenIn, tokenOut, amountIn, minAmountOut, salt)
        );
        require(commits[commitment] != 0, "No commit");
        require(block.number >= commits[commitment] + REVEAL_DELAY, "Too early");
        delete commits[commitment];
        // execute swap
    }
}

Tight Slippage + Deadline

Users with minAmountOut = 0 or very high slippage—easy victims. Protocol should:

  • Calculate recommended slippage based on pool depth
  • Warn about high slippage in UI
  • Short deadline (not more than 10-15 minutes) to prevent stale transactions

MEV Blocker Integration

For frontend: route transactions through MEV Blocker (CoW Protocol) or Flashbots Protect instead of public mempool. Only trusted builders see transactions, not public searchers.

// Send via Flashbots Protect RPC
const provider = new ethers.JsonRpcProvider(
    'https://rpc.flashbots.net',
    1 // mainnet
);

This doesn't require smart contract changes—just RPC endpoint switch.

TWAP Oracle Instead of Spot Price

For protocols using AMM prices: Uniswap v3 TWAP (Time-Weighted Average Price) resistant to flash loan manipulation—can't shift TWAP in one block.

function getTWAP(address pool, uint32 secondsAgo) public view returns (uint256 price) {
    uint32[] memory secondsAgos = new uint32[](2);
    secondsAgos[0] = secondsAgo;
    secondsAgos[1] = 0;

    (int56[] memory tickCumulatives,) = IUniswapV3Pool(pool).observe(secondsAgos);
    int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
    int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(secondsAgo)));

    price = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);
}

Analytics: Monitoring and Reporting

MEV Dashboard: visualization of historical MEV events per protocol. Answers: how many users suffered from sandwich last month? Which pools most attacked? What's average damage per event?

Eigenφhi, Flashbots MEV-Explore, EigenPhi—public tools for historical analysis. For custom monitoring—The Graph subgraph + Dune Analytics.

Attack Type Detection Method Complexity Accuracy
Sandwich Block pattern Medium High
JIT liquidity LP + swap timing Medium High
Oracle manipulation Price deviation + flash loan High Medium
Liquidation front-run Race transactions Low High
Time-bandit Reorg detection Very high Low

Stack and Timeline

Backend: Python (detection engine) + TypeScript (real-time mempool). Archive node access (Erigon self-hosted or Alchemy archive). PostgreSQL for event storage. Dune Analytics integration for visualization.

Development full-stack MEV detection system: 10-16 weeks. Post-hoc on-chain sandwich analysis only: 4-6 weeks. Flashbots Protect frontend integration: 1-2 days.