Sandwich attack protection 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
Sandwich attack protection system development
Medium
from 1 business day to 3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Sandwich Attack Protection System

Sandwich attack—form of MEV (Maximal Extractable Value), where attacker sees pending user transaction in mempool, places their transaction with higher gas before it (frontrun) and one right after (backrun). User gets significantly worse exchange rate, difference goes to attacker.

In Uniswap-like AMM this works straightforwardly: attacker buys token before user (price rises), sells after (price returns down). Difference—MEV bot profit. According to EigenPhi, major sandwich bots on Ethereum earned hundreds of thousands of dollars weekly at expense of regular users.

Attack Mechanics and Weak Points

To understand protection, must clearly understand where vulnerability lies.

Public mempool: transactions visible to all before block inclusion. MEV bot reads swap parameters (token address, amount, slippage tolerance) and calculates attack profitability.

High slippage tolerance: user specifies acceptable price deviation (e.g., 5%). Higher tolerance—wider sandwich window. Swap with 1% slippage much harder to attack.

Deterministic price impact: for AMM with known formula (x*y=k or stable curve) attacker precisely calculates how much to buy before victim for maximum profit.

User-Level and Protocol-Level Defenses

Private RPC and MEV-Protection Providers

Simplest protection: send transactions not to public mempool but directly to block builders through private channels.

Flashbots Protect: transactions go into Flashbots bundle. Attacker doesn't see them in public mempool. Free for user.

MEV Blocker: service from CoW Protocol. Transactions sent to multiple searchers who compete for best execution (refund part of MEV back to user).

Bloxroute: paid service with protected channels to miners/validators.

// Using Flashbots Protect via ethers.js
const { FlashbotsBundleProvider } = require('@flashbots/ethers-provider-bundle');
const { ethers } = require('ethers');

async function protectedSwap(swapParams, wallet, provider) {
    // Connect to Flashbots relay
    const flashbotsProvider = await FlashbotsBundleProvider.create(
        provider,
        wallet,  // auth signer (can be separate wallet)
        'https://relay.flashbots.net'
    );
    
    // Build swap transaction as usual
    const swapTx = await buildSwapTransaction(swapParams);
    
    // Send through Flashbots—transaction doesn't reach public mempool
    const bundleSubmission = await flashbotsProvider.sendPrivateTransaction(
        {
            signer: wallet,
            transaction: swapTx
        },
        { maxBlockNumber: (await provider.getBlockNumber()) + 10 }
    );
    
    const receipt = await bundleSubmission.wait();
    return receipt;
}

Minimize Slippage Tolerance

Protocols should recommend and apply reasonable slippage by default instead of inflated.

// In smart contract: hard limit on maximum slippage
contract SlippageProtectedRouter {
    uint256 public constant MAX_SLIPPAGE_BPS = 300; // 3% maximum
    
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external returns (uint256[] memory amounts) {
        // Calculate current price and check slippage
        uint256 expectedOut = getAmountOut(amountIn, path);
        uint256 slippageBps = (expectedOut - amountOutMin) * 10000 / expectedOut;
        
        require(slippageBps <= MAX_SLIPPAGE_BPS, "Slippage too high");
        
        return _executeSwap(amountIn, amountOutMin, path, to);
    }
}

Commit-Reveal Scheme

User in first transaction publishes commitment (hash of swap parameters), in second—reveals. MEV bots don't know parameters until second transaction, when too late to insert before.

contract CommitRevealSwap {
    mapping(bytes32 => Commitment) public commitments;
    
    struct Commitment {
        address user;
        uint256 blockNumber;
        bool revealed;
    }
    
    uint256 public constant MIN_BLOCKS_BEFORE_REVEAL = 1;
    uint256 public constant MAX_BLOCKS_BEFORE_REVEAL = 10;
    
    // Step 1: user publishes hash of parameters
    function commit(bytes32 commitHash) external {
        commitments[commitHash] = Commitment({
            user: msg.sender,
            blockNumber: block.number,
            revealed: false
        });
    }
    
    // Step 2: reveal and execute
    function reveal(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        bytes32 salt
    ) external {
        bytes32 commitHash = keccak256(abi.encodePacked(
            msg.sender, amountIn, amountOutMin, 
            keccak256(abi.encode(path)), salt
        ));
        
        Commitment storage commitment = commitments[commitHash];
        require(commitment.user == msg.sender, "Not your commit");
        require(!commitment.revealed, "Already revealed");
        require(
            block.number >= commitment.blockNumber + MIN_BLOCKS_BEFORE_REVEAL,
            "Too early"
        );
        require(
            block.number <= commitment.blockNumber + MAX_BLOCKS_BEFORE_REVEAL,
            "Expired"
        );
        
        commitment.revealed = true;
        _executeSwap(amountIn, amountOutMin, path, msg.sender);
    }
}

Downside: two-step process creates UX friction. Applicable for large swaps, inconvenient for frequent small operations.

Batch Auctions: Architectural Solution via CoW Protocol

CoW Protocol solves sandwich problem architecturally via batch auctions. Instead of immediate execution of each swap:

  1. Swap orders collected for period (several blocks)
  2. Off-chain solver finds optimal execution for entire batch (CoW—Coincidence of Wants: if A exchanges ETH for USDC and B exchanges USDC for ETH—can execute directly)
  3. Single transaction with batch results published on-chain
  4. All batch participants get uniform clearing price

In batch auction there's no point for sandwich: attacker can't insert between order and execution because they're separated in time and executed as batch.

Regular AMM:
Block N:   frontrun_buy → user_swap → backrun_sell
           (attacker earned at expense of user_swap)

CoW Batch Auction:
Block N:   submit_order(user1), submit_order(user2), ...
Block N+3: solver publishes settlement_tx with uniform price
           (no place for frontrun/backrun inside batch)

Dynamic Slippage Based on On-Chain Conditions

Smart contract or frontend can dynamically calculate reasonable slippage based on current market volatility and swap volume.

async function calculateSafeDynamicSlippage(
    tokenIn, tokenOut, amountIn, provider
) {
    // Get TWAP from Uniswap V3 oracle
    const [twapPrice, spotPrice] = await Promise.all([
        getTWAPPrice(tokenIn, tokenOut, 1800, provider),  // 30-min TWAP
        getSpotPrice(tokenIn, tokenOut, provider)
    ]);
    
    // Current spot deviation from TWAP
    const currentDeviation = Math.abs(spotPrice - twapPrice) / twapPrice;
    
    // Estimate price impact from our swap
    const priceImpact = await estimatePriceImpact(tokenIn, tokenOut, amountIn, provider);
    
    // Base slippage + volatility buffer
    const baseSlippage = priceImpact * 1.2;  // 20% buffer above price impact
    const volatilityBuffer = currentDeviation * 0.5;
    
    const recommendedSlippage = Math.min(
        baseSlippage + volatilityBuffer,
        0.03  // maximum 3%
    );
    
    return {
        recommendedSlippageBps: Math.ceil(recommendedSlippage * 10000),
        priceImpact: priceImpact,
        currentVolatility: currentDeviation
    };
}

Monitoring and Attack Detection

Protection system should include monitoring component: tracking specific MEV bots, user loss statistics, alerts on sandwich activity spikes.

// Detect sandwich pattern via transaction analysis in block
async function detectSandwichInBlock(blockNumber, provider, uniswapAddress) {
    const block = await provider.getBlock(blockNumber, true);
    const uniswapTxs = block.transactions.filter(
        tx => tx.to?.toLowerCase() === uniswapAddress.toLowerCase()
    );
    
    const sandwiches = [];
    
    for (let i = 1; i < uniswapTxs.length - 1; i++) {
        const prev = uniswapTxs[i - 1];
        const current = uniswapTxs[i];
        const next = uniswapTxs[i + 1];
        
        // Sandwich signs: prev and next from same address,
        // current—from different, opposite directions
        if (prev.from === next.from && prev.from !== current.from) {
            const prevDecoded = decodeSwap(prev.data);
            const nextDecoded = decodeSwap(next.data);
            
            // Frontrun buys what victim sells
            if (prevDecoded && nextDecoded && 
                prevDecoded.tokenIn === nextDecoded.tokenOut) {
                sandwiches.push({
                    attacker: prev.from,
                    victim: current.from,
                    frontrunTx: prev.hash,
                    victimTx: current.hash,
                    backrunTx: next.hash,
                    estimatedProfit: calculateSandwichProfit(prevDecoded, nextDecoded)
                });
            }
        }
    }
    
    return sandwiches;
}

Development Timeline

Integrating Flashbots Protect into existing swap interface—1–2 weeks. Commit-reveal mechanism in smart contract—2–3 weeks including tests. Dynamic slippage with TWAP oracle—2–3 weeks. Sandwich detection monitoring system—3–4 weeks. Full batch auction protocol per CoW model—2–4 months.