DEV Community

ohmygod
ohmygod

Posted on

The YieldBlox $10M Oracle Heist: How a Single Trade on Stellar's DEX Drained an Entire Lending Pool

Oracle manipulation is DeFi's original sin. But the YieldBlox exploit on February 22, 2026 showed that even in 2026, protocols keep making the same fatal mistake — trusting price feeds from illiquid markets without sanity checks.

A single attacker pumped USTRY from ~$1 to $107 on Stellar's DEX, used the inflated collateral to borrow $10M+ in USDC and XLM, then bridged everything to Ethereum before anyone could react.

Let's break down exactly how it happened, why it worked, and how to build oracle integrations that don't die to thin liquidity.

The Setup: Blend V2 and the Illiquidity Trap

YieldBlox DAO operated a lending pool on Blend V2, Stellar's isolated lending protocol. The pool accepted three assets:

  • Borrow assets: USDC, XLM
  • Collateral: USTRY (a US Treasury-backed token)

For pricing, the pool used Reflector, a VWAP (Volume-Weighted Average Price) oracle that pulled prices directly from SDEX — Stellar's native decentralized exchange.

Here's the critical detail: the USTRY/USDC market on SDEX had almost zero liquidity. Negligible volume. Paper-thin order books. This is the exact condition that makes VWAP oracles dangerous.

The Attack: 10 Transactions, $10M Gone

Step 1: Price Manipulation (Tx 1-2)

The attacker cleared the existing (minimal) USTRY/USDC liquidity on SDEX and placed abnormal orders, pushing USTRY's apparent price from ~$1.06 to ~$107 — a 100x inflation.

With virtually no other trades in the VWAP window, this single manipulated trade became the average.

Step 2: Oracle Update (Tx 3)

Reflector dutifully pulled the SDEX price and updated its feed. The oracle was working exactly as designed — it just reported what the market said. The problem was that the "market" was one attacker talking to themselves.

Step 3: Collateral Deposit + Borrow Round 1 (Tx 4-5)

The attacker deposited 12,881 USTRY as collateral. At the manipulated ~$107 price, this was valued at ~$1.38M instead of the real ~$13.7K.

Borrowed: 1,000,196 USDC (~$1M).

Step 4: Double Down — Borrow Round 2 (Tx 6-7)

Deposited 14,987,610 USTRY and borrowed 61.2 million XLM (~$9.85M).

Step 5: Bridge and Run (Tx 8-10)

The attacker bridged drained assets via Allbridge (Stellar → Base), then Across and Relay (Base → Ethereum). Total haul: ~$10.2M.

The Aftermath

  • $7.2M in XLM was frozen by Stellar Tier-1 validators
  • YieldBlox Security Council sent an on-chain bounty offer (10% for return of remaining funds)
  • Script3 confirmed full compensation for EURC, USDC, and XLM depositors
  • No other Blend pools were affected — this was a pool-operator configuration issue, not a Blend V2 bug

Why VWAP Oracles Fail in Thin Markets

VWAP calculates price as:

VWAP = Σ(Price_i × Volume_i) / Σ(Volume_i)
Enter fullscreen mode Exit fullscreen mode

When one trade dominates the window (because there are no other trades), VWAP collapses to the price of that single trade. The attacker essentially set the oracle price by being the only trader.

This is fundamentally different from TWAP (Time-Weighted Average Price) manipulation, which requires sustained capital across multiple blocks. VWAP manipulation in thin markets can be done in a single transaction.

Building Oracle Integrations That Survive

1. Liquidity Threshold Guards

Never trust a price from a market below a minimum liquidity threshold:

# Soroban (Stellar) pseudo-code for oracle consumer
def validate_oracle_price(asset, oracle_price, oracle_timestamp):
    # Check market depth before accepting price
    sdex_depth = get_sdex_orderbook_depth(asset, spread_bps=200)

    MIN_DEPTH_USD = 500_000  # $500K minimum depth

    if sdex_depth < MIN_DEPTH_USD:
        raise Error("Market too thin for reliable pricing")

    # Check staleness
    if current_time() - oracle_timestamp > MAX_STALENESS:
        raise Error("Oracle price too stale")

    return oracle_price
Enter fullscreen mode Exit fullscreen mode

2. Price Deviation Circuit Breakers

Reject oracle updates that deviate too far from historical prices:

// Solidity example — applicable to any EVM lending protocol
contract OracleGuard {
    uint256 public constant MAX_DEVIATION_BPS = 1000; // 10% max move
    uint256 public constant CIRCUIT_BREAKER_BPS = 5000; // 50% = halt

    mapping(address => uint256) public lastValidPrice;
    mapping(address => bool) public circuitBroken;

    function validatePrice(
        address asset,
        uint256 newPrice
    ) external returns (uint256) {
        uint256 lastPrice = lastValidPrice[asset];

        if (lastPrice == 0) {
            lastValidPrice[asset] = newPrice;
            return newPrice;
        }

        uint256 deviation = newPrice > lastPrice
            ? ((newPrice - lastPrice) * 10000) / lastPrice
            : ((lastPrice - newPrice) * 10000) / lastPrice;

        // Hard circuit breaker — 50%+ move = freeze
        if (deviation > CIRCUIT_BREAKER_BPS) {
            circuitBroken[asset] = true;
            emit CircuitBroken(asset, lastPrice, newPrice);
            return lastPrice; // Return stale price, halt new borrows
        }

        // Soft cap — 10%+ move = use dampened price
        if (deviation > MAX_DEVIATION_BPS) {
            uint256 dampened = newPrice > lastPrice
                ? lastPrice + (lastPrice * MAX_DEVIATION_BPS / 10000)
                : lastPrice - (lastPrice * MAX_DEVIATION_BPS / 10000);
            lastValidPrice[asset] = dampened;
            emit PriceDampened(asset, newPrice, dampened);
            return dampened;
        }

        lastValidPrice[asset] = newPrice;
        return newPrice;
    }

    event CircuitBroken(address asset, uint256 lastPrice, uint256 newPrice);
    event PriceDampened(address asset, uint256 rawPrice, uint256 dampened);
}
Enter fullscreen mode Exit fullscreen mode

3. Multi-Source Oracle Aggregation

Never rely on a single price source. Cross-reference multiple feeds:

# Multi-source oracle pattern
def get_validated_price(asset):
    sources = {
        "sdex_vwap": get_reflector_price(asset),
        "cex_feed": get_cex_price(asset),     # Binance/Coinbase
        "chainlink": get_chainlink_price(asset),
    }

    prices = [p for p in sources.values() if p is not None]

    if len(prices) < 2:
        raise Error("Insufficient price sources")

    median = sorted(prices)[len(prices) // 2]

    # Reject any source deviating >5% from median
    valid = [p for p in prices if abs(p - median) / median < 0.05]

    if len(valid) < 2:
        raise Error("Price sources diverge — possible manipulation")

    return sum(valid) / len(valid)
Enter fullscreen mode Exit fullscreen mode

4. Borrow Power Caps per Collateral Type

Even with good oracles, limit exposure to exotic collateral:

contract LendingPoolGuards {
    struct CollateralConfig {
        uint256 maxBorrowPowerUSD;  // Hard cap on total borrows against this asset
        uint256 currentBorrowed;
        uint256 collateralFactor;   // e.g., 50% for exotic assets
    }

    mapping(address => CollateralConfig) public collateralConfigs;

    function borrow(
        address collateral,
        uint256 borrowAmount,
        uint256 borrowValueUSD
    ) external {
        CollateralConfig storage config = collateralConfigs[collateral];

        require(
            config.currentBorrowed + borrowValueUSD <= config.maxBorrowPowerUSD,
            "Collateral borrow cap reached"
        );

        config.currentBorrowed += borrowValueUSD;
        // ... execute borrow
    }
}
Enter fullscreen mode Exit fullscreen mode

For the YieldBlox case, a $100K borrow cap on USTRY collateral would have limited the attack to $100K instead of $10M.

The Oracle Manipulation Audit Checklist

Before you ship any lending protocol or collateral integration:

Check Question YieldBlox Failed?
Liquidity depth Does the oracle's source market have >$500K depth? ✅ Failed
VWAP window Can one trade dominate the averaging window? ✅ Failed
Deviation limits Are price moves >10% dampened or rejected? ✅ Failed
Multi-source Does the protocol cross-reference 2+ price feeds? ✅ Failed
Borrow caps Are per-collateral borrow limits enforced? ✅ Failed
Collateral factor Is the CF appropriately low for exotic/illiquid assets? ✅ Failed
Monitoring Do abnormal price moves trigger alerts before borrows execute? ✅ Failed

YieldBlox failed every single check. The USTRY market had negligible liquidity, no deviation guards, single-source pricing, no borrow caps, and no monitoring. It was a textbook setup for oracle manipulation.

The Broader Pattern

This wasn't new. Oracle manipulation has been the #1 DeFi attack vector since 2020:

  • Mango Markets (2022): $114M via Mango token price manipulation on Serum
  • Euler Finance (2023): $197M via flash loan oracle manipulation
  • BonqDAO (2023): $120M via AllianceBlock oracle manipulation
  • YieldBlox (2026): $10.2M via USTRY VWAP manipulation on SDEX

The pattern is always the same: illiquid collateral + trusting oracle + no sanity checks = exploit.

Key Takeaways

  1. VWAP oracles are dangerous in thin markets — a single trade can set the price
  2. Pool operators are the security boundary in isolated lending protocols — the protocol can be sound while individual pools are misconfigured
  3. Exotic collateral needs aggressive guardrails — low collateral factors, borrow caps, liquidity thresholds
  4. Cross-chain bridging enables fast exits — the attacker was on Ethereum within minutes
  5. Validator-level asset freezing on Stellar saved $7.2M — a unique feature not available on most chains

The lesson isn't "don't use VWAP oracles." It's "don't use any oracle without validating the assumptions it depends on." Every oracle has failure modes. Your job is to make sure those failure modes can't drain your protocol.


DreamWork Security publishes weekly DeFi security research. Follow for vulnerability analysis, audit tool reviews, and security best practices.

Top comments (0)