Solana

Solana Beta is live. Try BoltRPC Solana endpoints free - start your trial now.

MEV Bots and RPC: Infrastructure Requirements for Searchers

What RPC infrastructure MEV searchers actually need: latency requirements, mempool access, Flashbots endpoint configuration, WebSocket patterns and provider selection criteria.

BoltRPC
BoltRPC Team
7 min read
MEV Bots and RPC: Infrastructure Requirements for Searchers

MEV Bots and RPC: Infrastructure Requirements for Searchers

Most MEV guides focus on strategy: how sandwich attacks work, how to find arbitrage opportunities, how to write Flashbots bundles. This guide focuses on the infrastructure layer that all of those strategies depend on: RPC.

The quality of your RPC setup determines whether you see opportunities, how fast you can act on them, and whether your transactions land. Getting the strategy right and the infrastructure wrong means losing to searchers with faster connections.


Why RPC Is the MEV Bottleneck

MEV extraction is a competition. Every searcher monitoring the same mempool is racing to:

  1. See an opportunity first
  2. Simulate a response transaction fastest
  3. Get a bundle or transaction confirmed in the next block

At each step, RPC performance is either your advantage or your bottleneck.

Step 1: Seeing opportunities: Opportunities arrive via new blocks (eth_subscribenewHeads) and pending transactions (eth_subscribenewPendingTransactions). A WebSocket subscription with lower latency sees these earlier.

Step 2: Simulating responses: Once you identify an opportunity, you simulate your response transaction using eth_call (to check profitability) and eth_estimateGas (to ensure it would succeed). These are synchronous RPC calls. Every millisecond of RPC latency adds to your simulation time.

Step 3: Landing the transaction: Private mempool submission (via Flashbots or equivalent) goes through a separate RPC endpoint. The speed and reliability of this connection determines whether your bundle reaches the block builder in time.


Required RPC Connections

A production MEV setup typically requires three separate RPC connections:

1. Public RPC (HTTP)
   → eth_call simulations
   → eth_estimateGas
   → eth_getBalance, eth_getCode
   → General state reads

2. WebSocket (Public mempool)
   → eth_subscribe newHeads (new blocks)
   → eth_subscribe newPendingTransactions (mempool)

3. Flashbots/Private RPC
   → eth_sendBundle (private bundle submission)
   → eth_callBundle (bundle simulation)
   → mev_sendBundle

WebSocket Setup for Mempool Monitoring

import { ethers } from 'ethers';

const wsProvider = new ethers.WebSocketProvider(
  'wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY'
);

// Monitor new blocks: recalculate positions on every block
wsProvider.on('block', async (blockNumber) => {
  console.log(`New block: ${blockNumber}`);
  await scanForOpportunities(blockNumber);
});

// Monitor pending transactions: the mempool view
// Note: not all providers expose full pending tx data
wsProvider.on('pending', async (txHash) => {
  // Get full transaction data
  const tx = await wsProvider.getTransaction(txHash);
  if (!tx) return;

  // Check if this transaction creates an MEV opportunity
  const opportunity = await analyzeTransaction(tx);
  if (opportunity) {
    await respondToOpportunity(opportunity, tx);
  }
});

Important: Not all RPC providers expose newPendingTransactions with full transaction data. Some return only hashes, requiring a separate eth_getTransactionByHash call per transaction. This adds latency. Verify your provider’s mempool support before committing to their infrastructure.


Fast Transaction Simulation with eth_call

Before submitting a bundle, simulate profitability using eth_call:

async function simulateArbitrage(path, amountIn) {
  // Encode the arbitrage call
  const callData = arbitrageContract.interface.encodeFunctionData(
    'executeArbitrage',
    [path, amountIn]
  );

  try {
    const result = await provider.call({
      to: ARBITRAGE_CONTRACT_ADDRESS,
      data: callData,
    });

    // Decode profit from result
    const decoded = arbitrageContract.interface.decodeFunctionResult(
      'executeArbitrage',
      result
    );

    return {
      profitable: decoded.profit > 0n,
      profit: decoded.profit,
      gasEstimate: await provider.estimateGas({
        to: ARBITRAGE_CONTRACT_ADDRESS,
        data: callData,
      }),
    };
  } catch (error) {
    // Transaction would revert — not profitable
    return { profitable: false, profit: 0n };
  }
}

Latency matters here: If simulation takes 50ms longer than your competitor’s, they submit before you. Run your RPC provider from a server in the same region as the nodes to minimize round-trip time.


Flashbots Integration

Flashbots lets you submit transaction bundles privately. They bypass the public mempool and go directly to block builders, preventing frontrunning of your own transactions.

import { FlashbotsBundleProvider } from '@flashbots/ethers-provider-bundle';

// Your standard RPC provider
const provider = new ethers.JsonRpcProvider(
  'https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY'
);

// Flashbots relay
const flashbotsProvider = await FlashbotsBundleProvider.create(
  provider,
  ethers.Wallet.createRandom(), // Flashbots reputation key (not your funds wallet)
  'https://relay.flashbots.net',
  'mainnet'
);

async function submitBundle(signedTransactions, targetBlock) {
  const bundle = signedTransactions.map(tx => ({
    signedTransaction: tx
  }));

  // Simulate bundle first
  const simulation = await flashbotsProvider.simulate(bundle, targetBlock);

  if ('error' in simulation) {
    console.error('Bundle simulation failed:', simulation.error);
    return null;
  }

  console.log(`Bundle profit: ${ethers.formatEther(simulation.totalMinerReward)} ETH`);

  // Submit to multiple blocks for higher inclusion probability
  const submission = await flashbotsProvider.sendRawBundle(bundle, targetBlock);
  return submission;
}

eth_callBundle: Simulate Before Submitting

Simulate your entire bundle atomically before submitting:

async function simulateBundle(transactions, blockNumber) {
  const simulation = await flashbotsProvider.simulate(
    transactions.map(tx => ({ signedTransaction: tx })),
    blockNumber,
    blockNumber,  // state block: use current
  );

  if ('error' in simulation) {
    throw new Error(`Bundle reverts: ${simulation.error.message}`);
  }

  const totalGasUsed = simulation.results.reduce(
    (sum, tx) => sum + tx.gasUsed, 0n
  );

  const totalProfit = simulation.coinbaseDiff;

  console.log(`Gas used: ${totalGasUsed}`);
  console.log(`Profit: ${ethers.formatEther(totalProfit)} ETH`);

  // Only submit if profitable after gas
  const gasCost = totalGasUsed * simulation.results[0].gasPrice;
  return totalProfit > gasCost;
}

Multi-Block Submission Strategy

A single block submission has low inclusion probability. Submit to multiple consecutive blocks:

async function submitToMultipleBlocks(bundle, currentBlock, blocksAhead = 3) {
  const submissions = [];

  for (let i = 1; i <= blocksAhead; i++) {
    const targetBlock = currentBlock + i;
    const submission = await flashbotsProvider.sendRawBundle(bundle, targetBlock);
    submissions.push({ targetBlock, submission });
  }

  // Wait for any inclusion
  const results = await Promise.allSettled(
    submissions.map(({ submission }) => submission.wait())
  );

  const included = results.find(r => r.status === 'fulfilled' && r.value === 1);
  return included ? 'included' : 'not included';
}

RPC Provider Selection for MEV

Not all RPC providers are equal for MEV use cases. Key criteria:

RequirementWhy it matters
WebSocket with full mempoolSee pending transactions with data, not just hashes
Low HTTP latencyFaster eth_call simulations
High rate limitsMEV bots make thousands of calls per block
Archive accessSome strategies require historical state
No method restrictionsdebug_traceTransaction, eth_callBundle may be needed
Geographic proximityServer in same region as your RPC = lower RTT
Reliable uptimeDowntime during active markets = missed opportunities

Geographic matters more for MEV than any other use case. A 10ms improvement in round-trip time can be the difference between landing and losing a bundle.


Monitoring Your MEV Infrastructure

class MevInfraMonitor {
  constructor(providers) {
    this.providers = providers;
    this.latencies = {};
  }

  async measureLatency(name, provider) {
    const start = Date.now();
    await provider.getBlockNumber();
    this.latencies[name] = Date.now() - start;
  }

  async checkAll() {
    await Promise.all(
      Object.entries(this.providers).map(([name, provider]) =>
        this.measureLatency(name, provider)
      )
    );

    console.table(this.latencies);

    // Alert if primary is slow
    if (this.latencies.primary > 100) {
      console.warn(`Primary RPC latency elevated: ${this.latencies.primary}ms`);
    }
  }
}

const monitor = new MevInfraMonitor({
  primary: primaryProvider,
  backup: backupProvider,
});

// Check every 30 seconds
setInterval(() => monitor.checkAll(), 30000);

FAQ

Do I need a dedicated node for MEV?

Dedicated nodes give the lowest possible latency and are used by top-tier searchers. For most strategies, a high-performance managed provider with low latency is sufficient. The economics depend on how much profit you are extracting. Dedicated node costs ($500-2,000/month) only make sense if your MEV profit justifies them.

Does BoltRPC support mempool access?

BoltRPC provides standard eth_subscribe WebSocket access including newPendingTransactions. Check your plan limits for pending transaction subscription volume, as mempool-heavy strategies generate high call rates.

What is the difference between Flashbots and a standard RPC?

A standard RPC submits transactions to the public mempool. Anyone can see them before inclusion, enabling frontrunning. Flashbots routes bundles directly to block builders via the MEV-Boost relay network, bypassing the public mempool. Your transactions are private until included in a block.

Can my MEV bot run on multiple chains simultaneously?

Yes. Run one connection set per chain. Each chain needs its own WebSocket for mempool/block monitoring and its own HTTP provider for simulations. Most chains have smaller searcher communities than Ethereum mainnet, making cross-chain opportunities less competitive.

What happens if my RPC provider goes down mid-block?

You miss everything that happens in the blocks where you are disconnected. For active strategies, implement automatic failover to a backup provider as described in our RPC Failover Guide. Recovery time directly costs profit.


Start your free 2-week trial: trial.boltrpc.io

Related: WebSocket vs HTTP for Blockchain RPC | Optimizing RPC Calls for DeFi

Frequently asked questions

Ready to build with high-performance RPC?

Start your free trial today. No credit card required. Access 20+ networks instantly.

Disclaimer: The content in this article is for informational purposes only and does not constitute financial, legal, or technical advice. Code examples and configurations are provided as-is. Always verify information with official documentation and test thoroughly in your own environment before deploying to production.

Continue reading