DEV Community

ohmygod
ohmygod

Posted on

The Skip-Vote Gap: How Solana's SIMD-0370 Dynamic Blocks Create a Finality Blind Spot That DeFi Protocols Must Audit Now

On September 29, 2025, Jump Crypto's Firedancer team proposed SIMD-0370: remove Solana's fixed 60-million-CU block limit and let each leader pack as many transactions as hardware allows. The promise — throughput that scales with silicon instead of committee votes — earned an immediate governance nod. What the proposal's cheerful benchmarks didn't emphasize is the security consequence: when a Firedancer leader fills a block that an Agave validator can't verify within 400 ms, that validator skips its vote. And every skipped vote widens a finality blind spot that DeFi protocols assume doesn't exist.

This article maps the skip-vote gap, shows three concrete attack surfaces it opens for DeFi, and provides a Foundry-based test harness to prove your protocol isn't exposed.


1. How Dynamic Blocks Change the Finality Model

Before SIMD-0370

Property Value
Block compute cap 60 M CU (proposed ↑ to 100 M)
Slot time 400 ms
Vote assumption All validators can verify within slot
Effective finality ~400 ms (optimistic confirmation)

Every validator could verify every block. Optimistic confirmation — "the block is final once 2/3 of stake votes" — happened within one or two slots because verification never exceeded capacity.

After SIMD-0370

Property Value
Block compute cap None (leader-determined)
Slot time 400 ms
Vote assumption Only validators whose hardware keeps up
Effective finality Variable — depends on which validators skip

A Firedancer leader running on a 64-core AMD EPYC with 512 GB RAM might produce a 200 M CU block. An Agave validator on a 32-core machine with 128 GB RAM might need 900 ms to verify it — and therefore skips the vote. If enough stake is on slower hardware, finality stretches from 400 ms to potentially seconds.


2. The Three Attack Surfaces

Attack Surface 1: Liquidation Front-Running via Verification Lag

The assumption every lending protocol makes: "If a price oracle updates in slot N, a liquidation in slot N+1 will be final."

What SIMD-0370 breaks: If the oracle update lands in a fat block that 30% of stake can't verify in time, those validators skip voting. The block is included but not finalized. During this verification lag window, an attacker can:

  1. Observe the oracle update in the unfinalized block
  2. Submit a competing transaction in the next slot that front-runs the liquidation
  3. If enough validators catch up and vote, the fat block finalizes — but the attacker's front-run in slot N+1 executes first on their local view

Real-world scenario: A Solana lending protocol with 400-ms finality assumptions gets a Chainlink push oracle update in a 180 M CU block. The block takes 1.2 seconds for 25% of stake to verify. During those 800 ms of extra verification time, an MEV searcher observes the price movement and front-runs the liquidation, extracting the liquidation bonus.

// VULNERABLE: Assumes instant finality
pub fn liquidate(ctx: Context<Liquidate>) -> Result<()> {
    let oracle_price = ctx.accounts.oracle.price; // Reads oracle from current slot
    require!(
        ctx.accounts.position.is_undercollateralized(oracle_price),
        ErrorCode::NotLiquidatable
    );
    // Proceeds with liquidation — no slot-lag check
    execute_liquidation(ctx)
}
Enter fullscreen mode Exit fullscreen mode
// HARDENED: Adds slot-freshness guard
pub fn liquidate(ctx: Context<Liquidate>) -> Result<()> {
    let current_slot = Clock::get()?.slot;
    let oracle_slot = ctx.accounts.oracle.last_update_slot;

    // Require oracle update is at least 2 slots old (survived verification)
    require!(
        current_slot.saturating_sub(oracle_slot) >= 2,
        ErrorCode::OracleUpdateTooFresh
    );

    let oracle_price = ctx.accounts.oracle.price;
    require!(
        ctx.accounts.position.is_undercollateralized(oracle_price),
        ErrorCode::NotLiquidatable
    );
    execute_liquidation(ctx)
}
Enter fullscreen mode Exit fullscreen mode

Attack Surface 2: Bridge Latency Exploits

Cross-chain bridges on Solana typically confirm finality by counting votes. With skip-votes, a bridge might see 52% stake confirming (enough for its threshold) while 15% of stake is still verifying a fat block. If those delayed validators eventually vote differently due to a fork, the bridge has accepted a transaction that gets reorganized.

The pattern:

Slot 100: Fat block (200M CU) — bridge deposit included
Slot 101: Bridge relayer sees 52% stake votes, triggers mint on destination chain
Slot 102: Delayed validators finish verifying, 8% vote for a competing fork
Result: Bridge deposit reorganized on Solana, but mint already executed on Ethereum
Enter fullscreen mode Exit fullscreen mode

Defense: Bridge protocols must increase their confirmation threshold from simple majority to supermajority + slot buffer:

// VULNERABLE bridge confirmation
const CONFIRMATION_THRESHOLD: f64 = 0.52; // Simple majority
const MIN_SLOTS_AFTER_INCLUSION: u64 = 1;

// HARDENED bridge confirmation
const CONFIRMATION_THRESHOLD: f64 = 0.72; // Well above 2/3
const MIN_SLOTS_AFTER_INCLUSION: u64 = 4;  // ~1.6s buffer for fat blocks
const MAX_BLOCK_CU_FOR_FAST_PATH: u64 = 80_000_000; // Only fast-path small blocks
Enter fullscreen mode Exit fullscreen mode

Attack Surface 3: Governance Snapshot Manipulation

Solana governance protocols (SPL Governance, Realms) take token-balance snapshots at specific slots. If the snapshot slot contains a fat block:

  1. Some validators haven't verified it yet
  2. An attacker submits a large token transfer in the fat block
  3. The transfer is "included" but not universally verified
  4. The governance snapshot captures the attacker's inflated balance
  5. Even if the fat block eventually finalizes normally, the timing of the snapshot relative to verification lag gives the attacker a window

Defense: Governance snapshots should use a lookback window of at least 4 slots, never the current slot:

pub fn create_proposal(ctx: Context<CreateProposal>) -> Result<()> {
    let current_slot = Clock::get()?.slot;
    // Snapshot from 4 slots ago — guaranteed finalized even with fat blocks
    let snapshot_slot = current_slot.saturating_sub(4);
    let voting_power = get_balance_at_slot(
        &ctx.accounts.token_account, 
        snapshot_slot
    )?;
    // ...
}
Enter fullscreen mode Exit fullscreen mode

3. Quantifying the Risk: When Does Skip-Vote Actually Matter?

Not every fat block creates an exploitable gap. The risk depends on three variables:

Variable Low Risk High Risk
Block CU size < 80M > 150M
% stake on Agave < 20% > 40%
Time-sensitivity of protocol Spot DEX swaps Liquidations, bridges, governance

As of March 2026, approximately 35% of Solana stake still runs on Agave clients. While the Foundation's updated delegation criteria push validators toward Firedancer, the transition is gradual. During this multi-client transition period, fat blocks from Firedancer leaders will reliably trigger skip-votes from a significant minority of stake.


4. A Foundry-Equivalent Detection Approach

While Solana doesn't have Foundry's fork testing, you can simulate verification lag in a Bankrun test:

// bankrun-skip-vote-test.ts
import { start } from 'solana-bankrun';
import { PublicKey, Transaction } from '@solana/web3.js';

describe('Skip-Vote Finality Gap', () => {
  it('should reject liquidation when oracle update is too fresh', async () => {
    const context = await start([], []);
    const client = context.banksClient;

    // Simulate: oracle updates in current slot
    const oracleUpdateTx = createOracleUpdateTx(newPrice);
    await client.processTransaction(oracleUpdateTx);

    // Attempt liquidation in same slot — should fail with slot-freshness guard
    const liquidateTx = createLiquidateTx(position);
    try {
      await client.processTransaction(liquidateTx);
      throw new Error('Liquidation should have been rejected');
    } catch (e) {
      expect(e.message).toContain('OracleUpdateTooFresh');
    }

    // Advance 2 slots — simulates verification completion
    context.warpToSlot(context.lastBlockhash + 2n);

    // Now liquidation should succeed
    await client.processTransaction(createLiquidateTx(position));
  });

  it('should reject bridge confirmation below supermajority threshold', async () => {
    // Test that bridge requires 72% stake confirmation
    // and minimum 4-slot buffer after inclusion
    // ...
  });
});
Enter fullscreen mode Exit fullscreen mode

5. The Audit Checklist for SIMD-0370 Readiness

Every Solana DeFi protocol should answer these questions before dynamic blocks go live:

Oracle Consumers

  • [ ] Do you assume oracle updates are final within 1 slot?
  • [ ] Do you enforce a min_slots_since_update freshness check?
  • [ ] Can liquidations be front-run during the verification lag window?

Bridge Protocols

  • [ ] What is your vote-confirmation threshold? Is it > 67%?
  • [ ] Do you add a slot buffer before considering deposits final?
  • [ ] Do you differentiate fast-path (small blocks) vs. slow-path (fat blocks)?

Governance

  • [ ] Do snapshots reference the current slot or a lookback window?
  • [ ] Is the lookback window at least 4 slots (1.6 seconds)?
  • [ ] Can token transfers in a fat block influence a pending snapshot?

General

  • [ ] Does your program use valid_until_slot for time-sensitive instructions?
  • [ ] Do you set explicit slippage tolerances that account for variable finality?
  • [ ] Have you tested against blocks > 100M CU in your Bankrun harness?

6. What Solana Is Doing About It

The Solana Foundation has taken several steps to mitigate skip-vote risks:

  1. Updated delegation criteria now require validators to run Firedancer or Frankendancer 0.808.30014+, reducing the percentage of stake on verification-limited clients
  2. Timely Vote Credits (SIMD-0033), activated in November 2024, penalize late voters — but don't address the can't-verify-at-all case that SIMD-0370 introduces
  3. Alpenglow's Voterless Tower (SIMD-0326) redesigns the voting mechanism to be more resilient to variable verification times

None of these fully eliminate the gap. Until the validator set uniformly runs clients capable of verifying any block size within 400 ms, the skip-vote gap is an architectural reality that protocols must code around.


Conclusion

SIMD-0370 represents Solana's boldest scaling move since the original Proof-of-History design. Dynamic blocks will eventually make Solana the highest-throughput chain in production. But the transition period — where Firedancer leaders produce blocks that Agave validators can't keep up with — creates a finality model that's fundamentally different from what every existing DeFi protocol was built for.

The three attack surfaces — liquidation front-running, bridge latency exploitation, and governance snapshot manipulation — are all preventable with modest code changes. The audit checklist above gives every team a starting point. The protocols that adapt before SIMD-0370 hits mainnet will be fine. The ones that assume 400-ms finality is still universal will learn the hard way.


This article is part of my DeFi Security Research series. Follow @ohmygod for weekly deep-dives into smart contract vulnerabilities, audit techniques, and defense patterns across Solana and EVM ecosystems.

Top comments (0)