DEV Community

ohmygod
ohmygod

Posted on

The Solana Mempool Blind Spot: Why EVM-Style Runtime Monitoring Fails on SVM and How to Build Detection That Actually Works

Ethereum has Forta, Hypernative, and Hexagate — runtime monitoring platforms that watch the mempool, simulate pending transactions, and pause protocols before exploits land. They've collectively saved billions.

Solana has… almost nothing comparable. And the architectural reasons why tell us something important about the future of cross-chain security.

The Fundamental Problem: Solana Doesn't Have a Mempool

Let's be precise about what this means.

On Ethereum, transactions enter a public mempool where they sit — sometimes for seconds, sometimes minutes — waiting to be included in a block. This window is what makes pre-execution monitoring possible. Forta bots can simulate transactions against current state, Hypernative can flag anomalous patterns, and Hexagate can trigger automated responses before the damage is done.

On Solana, the transaction lifecycle looks fundamentally different:

User → RPC Node → Leader Validator → Block (400ms slots)
Enter fullscreen mode Exit fullscreen mode

There is no global mempool. Transactions are forwarded directly to the current leader validator via Gulf Stream (Solana's mempool-less transaction forwarding protocol). The leader processes them and includes them in the next slot. Average slot time: 400 milliseconds.

This means:

  1. No public pending transaction pool to monitor
  2. No simulation window before inclusion
  3. Sub-second finality eliminates reaction time
  4. Transaction ordering is leader-determined, not fee-auction-based

Every EVM monitoring tool's core assumption — "we can see and react to transactions before they're finalized" — is architecturally invalid on Solana.

What Actually Happened: Solana Exploits That Monitoring Could Have Caught

The Mango Markets Exploit ($114M, October 2022)

Avraham Eisenberg manipulated Mango Markets' oracle prices by inflating his MNGO-PERP position, then borrowed against the inflated collateral. The attack took multiple transactions over several minutes.

Could runtime monitoring have helped? Yes — but not mempool monitoring. The attack's on-chain footprint was visible after the first few transactions. A Solana-native monitor watching for:

  • Abnormal oracle price movements
  • Collateral ratio changes exceeding historical norms
  • Large borrowing against recently-inflated positions

...could have triggered a protocol pause after the first manipulation transaction landed, before the borrowing transactions completed.

The Crema Finance Exploit ($8.8M, July 2022)

An attacker used flash loans to manipulate tick data in Crema's concentrated liquidity pools, extracting fees from fabricated liquidity positions.

Detection signal: The flash loan + position manipulation + fee claim pattern was detectable from on-chain transaction logs within the same block. No mempool needed — but sub-second detection and response was required.

The Cashio Infinite Mint ($52M, March 2022)

A missing validation in Cashio's burn_tokens_and_withdraw instruction allowed an attacker to use a fake collateral account to mint unlimited tokens.

Detection signal: The minting rate of CASH tokens exceeded any historical pattern by orders of magnitude. A supply-monitoring detector would have flagged this immediately.

Building Solana-Native Exploit Detection: Four Layers

Since we can't monitor a mempool that doesn't exist, Solana exploit detection must be fundamentally different. Here's the architecture that works:

Layer 1: Transaction Stream Processing

Instead of watching a mempool, monitor the real-time stream of confirmed transactions. On Solana, this means subscribing to the validator's transaction feed:

// Using Solana's Geyser plugin interface for real-time tx streaming
pub struct ExploitDetectorPlugin;

impl GeyserPlugin for ExploitDetectorPlugin {
    fn transaction_update(
        &self,
        transaction: ReplicaTransactionInfoV2,
    ) -> Result<()> {
        // Every confirmed transaction hits this callback
        // within milliseconds of slot confirmation

        let instructions = &transaction.transaction.message().instructions;

        for ix in instructions {
            // Decode program-specific instructions
            if ix.program_id == known_defi_program {
                self.analyze_defi_instruction(ix, &transaction)?;
            }
        }
        Ok(())
    }
}
Enter fullscreen mode Exit fullscreen mode

The Geyser plugin interface lets you tap directly into the validator's transaction processing pipeline. This is the closest thing to "mempool monitoring" on Solana — you see transactions the moment they're confirmed, not before.

Latency: 10-50ms after slot confirmation. Not pre-execution, but fast enough to catch multi-transaction attacks before they complete.

Layer 2: State Change Monitoring

The real power of Solana monitoring comes from watching account state changes, not individual transactions. Most exploits produce anomalous state transitions:

# Pseudocode: State change anomaly detection
class StateMonitor:
    def __init__(self):
        self.baselines = {}  # Historical state change patterns

    def on_account_update(self, pubkey, old_state, new_state, slot):
        # Track token supply changes
        if is_mint_account(pubkey):
            supply_delta = new_state.supply - old_state.supply
            baseline = self.baselines.get(pubkey, {})

            # Flag if supply change exceeds 3 sigma of historical pattern
            if abs(supply_delta) > baseline.mean + 3 * baseline.std:
                self.alert(
                    severity=\"CRITICAL\",
                    msg=f\"Anomalous supply change: {supply_delta} tokens\",
                    pubkey=pubkey,
                    slot=slot
                )

        # Track TVL movements
        if is_vault_account(pubkey):
            tvl_delta = new_state.lamports - old_state.lamports
            if tvl_delta < -self.LARGE_WITHDRAWAL_THRESHOLD:
                self.alert(
                    severity=\"HIGH\",
                    msg=f\"Large vault withdrawal: {tvl_delta / 1e9} SOL\",
                    pubkey=pubkey,
                    slot=slot
                )
Enter fullscreen mode Exit fullscreen mode

Geyser plugins also provide account update notifications, making this highly efficient. You don't need to decode every transaction — just watch the accounts that matter.

Layer 3: Cross-Program Invocation (CPI) Graph Analysis

Many Solana exploits abuse CPI chains — calling into programs in unexpected sequences. Monitoring the CPI graph in real-time catches attacks that individual transaction analysis misses:

class CPIGraphMonitor:
    def __init__(self):
        self.known_cpi_patterns = self.load_baseline_patterns()

    def analyze_inner_instructions(self, tx):
        \"\"\"Build CPI call graph from inner instructions\"\"\"
        cpi_chain = []
        for inner_ix in tx.meta.inner_instructions:
            caller = inner_ix.instructions[0].program_id
            callee = inner_ix.instructions[-1].program_id
            cpi_chain.append((caller, callee))

        # Check for novel CPI patterns
        cpi_signature = tuple(cpi_chain)
        if cpi_signature not in self.known_cpi_patterns:
            # New CPI pattern — could be legitimate or exploit
            self.evaluate_novel_pattern(cpi_signature, tx)

    def evaluate_novel_pattern(self, pattern, tx):
        \"\"\"Score novel CPI patterns for exploit likelihood\"\"\"
        risk_score = 0

        # Flash loan initiation followed by oracle update
        if contains_flash_loan(pattern) and contains_oracle_update(pattern):
            risk_score += 80

        # Token mint in unexpected CPI context
        if contains_unexpected_mint(pattern):
            risk_score += 90

        # Authority transfer or delegation change
        if contains_authority_change(pattern):
            risk_score += 70

        if risk_score > 75:
            self.alert(severity=\"CRITICAL\", pattern=pattern, tx=tx)
Enter fullscreen mode Exit fullscreen mode

Layer 4: Economic Invariant Checking

The most powerful detection layer doesn't care about transaction details at all — it validates economic invariants that should never be violated:

class InvariantChecker:
    \"\"\"Protocol-specific invariant validators\"\"\"

    def check_lending_protocol(self, protocol_state):
        # Total borrows should never exceed total deposits
        if protocol_state.total_borrows > protocol_state.total_deposits:
            self.alert(\"CRITICAL: Borrows exceed deposits\")

        # Individual position health factor should be checked
        # against protocol-reported health
        for position in protocol_state.positions:
            recalculated_health = self.recalculate_health(
                position,
                independent_price_source=True
            )
            reported_health = position.health_factor

            if abs(recalculated_health - reported_health) > 0.05:
                self.alert(
                    f\"Health factor discrepancy: \"
                    f\"reported={reported_health}, \"
                    f\"calculated={recalculated_health}\"
                )

    def check_amm(self, pool_state):
        # Constant product invariant (with tolerance for fees)
        k_current = pool_state.reserve_a * pool_state.reserve_b
        k_expected = pool_state.last_known_k

        if k_current < k_expected * 0.99:  # More than 1% decrease
            self.alert(
                f\"AMM invariant violation: \"
                f\"k dropped from {k_expected} to {k_current}\"
            )
Enter fullscreen mode Exit fullscreen mode

The Response Problem: What Do You Do in 400ms?

Detection is only half the equation. On Ethereum, you might have 12 seconds (one block) to respond. On Solana, you have 400ms per slot, and the exploit transaction is already confirmed by the time you see it.

This changes the response strategy fundamentally:

Automated Protocol Pausing

The only viable response for single-transaction exploits is pre-authorized automated pausing:

// Program instruction: emergency pause
// Called by an authorized monitoring keypair
pub fn emergency_pause(ctx: Context<EmergencyPause>) -> Result<()> {
    require!(
        ctx.accounts.authority.key() == MONITORING_AUTHORITY,
        ErrorCode::Unauthorized
    );

    let protocol = &mut ctx.accounts.protocol_state;
    protocol.paused = true;
    protocol.pause_timestamp = Clock::get()?.unix_timestamp;
    protocol.pause_reason = PauseReason::AutomatedDetection;

    emit!(ProtocolPaused {
        authority: ctx.accounts.authority.key(),
        timestamp: protocol.pause_timestamp,
        reason: "Automated exploit detection trigger"
    });

    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

The monitoring system holds a keypair authorized to pause the protocol. When detection fires, it submits a pause transaction immediately. This is controversial — it's a centralization trade-off — but it's the only way to respond faster than an attacker on Solana.

Multi-Transaction Attack Interruption

For attacks that require multiple transactions (like Mango Markets), you have more time. The strategy:

  1. Detect anomaly after first transaction
  2. Submit pause transaction with high priority fee
  3. Alert human operators simultaneously
  4. Log all evidence for post-incident analysis

The key insight: most sophisticated exploits on Solana require 2-10 transactions. If your detection fires after transaction 1 and your pause lands before transaction 3, you've limited the damage.

Jito Bundle Awareness

Since March 2025, Jito bundles have become a significant factor. Attackers can bundle multiple exploit transactions together, ensuring atomic execution. This means:

  • All exploit transactions land in the same slot
  • No opportunity for inter-slot intervention
  • Detection must focus on prevention (invariant-enforced program design) rather than reaction

For bundle-based attacks, the only defense is program-level invariant checking:

// Inside your program: check invariants on every instruction
pub fn swap(ctx: Context<Swap>, amount_in: u64) -> Result<()> {
    // Pre-swap invariant check
    let k_before = ctx.accounts.pool.reserve_a
        .checked_mul(ctx.accounts.pool.reserve_b)
        .ok_or(ErrorCode::MathOverflow)?;

    // ... execute swap logic ...

    // Post-swap invariant check
    let k_after = ctx.accounts.pool.reserve_a
        .checked_mul(ctx.accounts.pool.reserve_b)
        .ok_or(ErrorCode::MathOverflow)?;

    require!(
        k_after >= k_before,
        ErrorCode::InvariantViolation
    );

    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

The Tooling Gap: What Exists Today

What's Available

  • Helius Webhooks: Real-time transaction notifications with program-level filtering. Good for Layer 1 (transaction stream) monitoring.
  • Jito ShredStream: Access to shreds before full slot confirmation. Closest thing to "mempool" monitoring on Solana, but requires validator-level infrastructure.
  • Geyser Plugins (Yellowstone/Dragon's Mouth): Low-latency account and transaction streaming. The foundation for serious monitoring infrastructure.
  • Sec3 (x-ray): Static analysis focused on pre-deployment vulnerability scanning, not runtime monitoring.

What's Missing

  • No Solana equivalent of Forta's decentralized detection network. No shared infrastructure for community-contributed detection bots.
  • No pre-execution simulation service comparable to Ethereum's eth_simulateTransaction ecosystem.
  • No standardized "circuit breaker" interface that protocols can implement for automated emergency responses.
  • No cross-protocol anomaly correlation. Each monitoring deployment operates in isolation.

A Practical Architecture for Protocol Teams

If you're building or maintaining a Solana DeFi protocol in 2026, here's the minimum viable monitoring setup:

┌─────────────────────────────────────────────┐
│              Validator Node                  │
│  ┌─────────────────────────────────────┐    │
│  │       Geyser Plugin (Yellowstone)    │    │
│  └──────────────┬──────────────────────┘    │
└─────────────────┼───────────────────────────┘
                  │ gRPC stream
                  ▼
┌─────────────────────────────────────────────┐
│           Detection Engine                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐    │
│  │ CPI Graph│ │ State    │ │ Invariant│    │
│  │ Monitor  │ │ Anomaly  │ │ Checker  │    │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘    │
│       └─────────┬───┴────────────┘          │
│                 ▼                            │
│  ┌──────────────────────────────────┐       │
│  │     Alert Aggregator & Scorer    │       │
│  └──────────────┬───────────────────┘       │
└─────────────────┼───────────────────────────┘
                  │
          ┌───────┼───────┐
          ▼       ▼       ▼
     ┌────────┐ ┌────┐ ┌──────────┐
     │Auto-   │ │Ops │ │Evidence  │
     │Pause TX│ │PD  │ │Logger    │
     └────────┘ └────┘ └──────────┘
Enter fullscreen mode Exit fullscreen mode

Estimated cost: Running a Geyser-enabled validator node plus detection infrastructure: $800-2,000/month. Significantly cheaper than the alternative — losing your TVL.

The Cross-Chain Future

The real opportunity is building monitoring infrastructure that works across both EVM and SVM architectures. A unified detection framework would:

  1. Normalize transaction data from both chains into a common event format
  2. Share detection logic where applicable (economic invariants are chain-agnostic)
  3. Correlate cross-chain attacks — bridge exploits often involve coordinated actions on both sides
  4. Enable portable security expertise — an auditor's detection rules should work everywhere

Hypernative is moving in this direction with multi-chain support. Forta has experimented with non-EVM chains. But nobody has built the Solana-native, validator-integrated monitoring layer that the ecosystem needs.

The protocol that builds this — an open, composable, Solana-native detection network with Geyser-level integration — will become as essential to Solana DeFi as Forta is to Ethereum.

Action Items

For protocol developers:

  1. Implement program-level invariant checks — they're your only defense against Jito bundle attacks
  2. Deploy Geyser-based monitoring with at minimum state change anomaly detection
  3. Pre-authorize an emergency pause keypair and build the automation
  4. Define your protocol's economic invariants explicitly and monitor them

For security researchers:

  1. Build Solana-specific detection bots — the field is wide open
  2. Study Jito bundle patterns for attack classification
  3. Contribute to open-source Geyser plugin detection frameworks
  4. Develop CPI graph analysis tools for audit workflows

For the ecosystem:

  1. Fund a decentralized Solana detection network (Forta-equivalent)
  2. Standardize a circuit breaker interface for Solana programs
  3. Build pre-execution simulation infrastructure at the RPC layer
  4. Create cross-chain monitoring standards

The mempool-based monitoring paradigm was a gift of Ethereum's architecture. Solana doesn't give us that gift. But the detection principles — anomaly detection, invariant checking, pattern recognition — are universal. We just need to rebuild the infrastructure from scratch.


This article is part of the DeFi Security Research series. Follow for weekly deep-dives into the vulnerabilities, tools, and practices shaping blockchain security in 2026.

Top comments (0)