Securite·10 min de lecture·Par Solingo

Post-Mortem — The Biggest DeFi Hacks of Q1 2026

$X million lost in Q1 2026. Recurring patterns, new attack vectors, and what we should learn.

# Post-Mortem — The Biggest DeFi Hacks of Q1 2026

Q1 2026 saw over $420 million stolen across five major exploits. The attacks were more sophisticated than previous years, but the root causes were depressingly familiar: oracle manipulation, signature handling errors, and composability assumptions.

Here is what happened, why it happened, and what we should learn.

1. HyperBridge Cross-Chain Exploit — $180M

What Happened

On January 14, an attacker drained $180M from HyperBridge, a cross-chain messaging protocol. The exploit targeted the signature verification system for cross-chain messages.

Root Cause

HyperBridge used ECDSA signatures to authorize cross-chain transfers. The signature included:

  • Source chain ID
  • Destination chain ID
  • Recipient address
  • Amount

But it did NOT include the contract address. This meant a signature valid on one chain could be replayed on another chain running a different version of the contract.

Vulnerable code:

function bridgeTokens(

uint256 srcChainId,

uint256 dstChainId,

address recipient,

uint256 amount,

bytes memory signature

) external {

bytes32 message = keccak256(abi.encodePacked(

srcChainId,

dstChainId,

recipient,

amount

));

address signer = recoverSigner(message, signature);

require(validators[signer], "Invalid signer");

// Transfer tokens...

}

The attacker:

  • Created a legitimate withdrawal on Chain A
  • Observed the validator signature
  • Replayed the signature on Chain B with a modified contract
  • Drained funds by repeatedly replaying across forks
  • Fix

    Include the contract address in the signature:

    bytes32 message = keccak256(abi.encodePacked(
    

    address(this), // Add contract address

    srcChainId,

    dstChainId,

    recipient,

    amount

    ));

    Better yet, use EIP-712 with a domain separator that includes address(this) and block.chainid.

    2. YieldVault Oracle Manipulation — $95M

    What Happened

    On February 3, YieldVault, a leveraged yield farming protocol, was exploited via a flash loan price manipulation attack.

    Root Cause

    YieldVault used a TWAP oracle with a 10-minute window. The attacker discovered they could manipulate the price within a single block using a flash loan, then trigger liquidations before the TWAP adjusted.

    Attack flow:

  • Flash loan 500M USDC
  • Buy massive amount of YVT token → price spikes 40%
  • Deposit collateral and borrow against inflated price
  • Sell YVT → price crashes
  • Liquidate undercollateralized positions (including their own)
  • Repeat across 15 blocks
  • Repay flash loan, keep profit
  • Vulnerable code:

    function getPrice() public view returns (uint256) {
    

    // 10-minute TWAP

    return oracle.getTWAP(address(yvtToken), 600);

    }

    function borrow(uint256 amount) external {

    uint256 collateralValue = userCollateral[msg.sender] * getPrice();

    require(collateralValue >= amount * 150 / 100, "Undercollateralized");

    // ...

    }

    The TWAP was too short. 10 minutes can be manipulated by a coordinated attacker with enough capital.

    Fix

    Use a longer TWAP (24 hours) or, better, use Chainlink or a decentralized oracle with manipulation resistance:

    import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
    
    

    function getPrice() public view returns (uint256) {

    (

    uint80 roundId,

    int256 price,

    ,

    uint256 updatedAt,

    uint80 answeredInRound

    ) = priceFeed.latestRoundData();

    require(updatedAt > block.timestamp - 3600, "Stale price");

    require(answeredInRound >= roundId, "Stale round");

    return uint256(price);

    }

    3. StakeFi Restaking Slashing Griefing — $70M

    What Happened

    StakeFi, a liquid restaking protocol, suffered a slashing event that cascaded across 12,000 validators. The attacker triggered mass slashing by intentionally submitting conflicting attestations.

    Root Cause

    StakeFi allowed anyone to register as an operator without economic security. An attacker registered 500 "operators" with minimal stake, then intentionally misbehaved to trigger slashing on all delegators.

    The slashing penalty was:

    • 1 ETH per validator for the attacker (low cost)
    • 50% of staked assets for delegators (high cost)

    The attacker lost 500 ETH but caused $70M in losses to delegators, then shorted the StakeFi token to profit.

    Vulnerable design:

    function registerOperator() external payable {
    

    require(msg.value >= 0.1 ether, "Min stake");

    operators[msg.sender] = Operator({

    stake: msg.value,

    active: true

    });

    }

    No economic security requirement. Operators could grief delegators at minimal cost.

    Fix

    Require operators to have skin in the game:

    function registerOperator() external payable {
    

    require(msg.value >= 100 ether, "Min stake 100 ETH");

    require(reputation[msg.sender] > threshold, "Reputation too low");

    operators[msg.sender] = Operator({

    stake: msg.value,

    active: true

    });

    }

    Add reputation systems, slashing caps, and operator whitelisting.

    4. LiquidStake LST Depeg Cascade — $50M

    What Happened

    On February 28, LiquidStake's LST token depegged from 1:1 with ETH, triggering a cascade of liquidations across DeFi protocols. The depeg started at 2%, then spiraled to 40% within an hour.

    Root Cause

    LiquidStake had a withdrawal queue with a 7-day delay. When a large holder tried to exit, they sold LST on the open market instead of waiting. This triggered:

  • LST price drops to 0.98 ETH
  • Liquidations on Aave, Compound (LST used as collateral)
  • More LST dumped to cover liquidations
  • Price spirals to 0.60 ETH
  • Protocol insolvent
  • Vulnerable design:

    function withdraw(uint256 shares) external {
    

    withdrawQueue.push(WithdrawRequest({

    user: msg.sender,

    shares: shares,

    timestamp: block.timestamp

    }));

    }

    function processWithdrawals() external {

    for (uint i = 0; i < withdrawQueue.length; i++) {

    if (block.timestamp >= withdrawQueue[i].timestamp + 7 days) {

    // Process withdrawal

    }

    }

    }

    No instant withdrawal option for small amounts. Users were forced to sell on the market.

    Fix

    Add instant withdrawals for small amounts, with a fee:

    function instantWithdraw(uint256 shares) external {
    

    require(shares <= maxInstantWithdraw, "Amount too large");

    uint256 fee = shares * 50 / 10000; // 0.5% fee

    uint256 ethAmount = (shares - fee) * getETHPerShare() / 1e18;

    _burn(msg.sender, shares);

    payable(msg.sender).transfer(ethAmount);

    }

    5. IntentSwap Cross-Chain Intent Frontrun — $25M

    What Happened

    IntentSwap, a cross-chain intent protocol, was exploited via a frontrunning attack on unfilled intents.

    Root Cause

    IntentSwap allowed users to post intents (e.g., "swap 1000 USDC on Arbitrum for ETH on Optimism"). Fillers would compete to execute the intent.

    The problem: intents were public, and there was no slippage protection. An attacker:

  • Monitored the mempool for high-value intents
  • Frontran the intent by moving the price on the destination chain
  • Filled the intent at the manipulated price
  • Kept the difference
  • Vulnerable code:

    struct Intent {
    

    address user;

    uint256 amountIn;

    address tokenIn;

    address tokenOut;

    uint256 srcChainId;

    uint256 dstChainId;

    }

    function fillIntent(Intent memory intent) external {

    // No slippage check!

    uint256 amountOut = swap(intent.tokenIn, intent.tokenOut, intent.amountIn);

    transferCrossChain(intent.user, intent.tokenOut, amountOut, intent.dstChainId);

    }

    Fix

    Add a minimum output amount (slippage protection):

    struct Intent {
    

    address user;

    uint256 amountIn;

    address tokenIn;

    address tokenOut;

    uint256 minAmountOut; // NEW

    uint256 srcChainId;

    uint256 dstChainId;

    uint256 deadline; // NEW

    }

    function fillIntent(Intent memory intent) external {

    require(block.timestamp <= intent.deadline, "Expired");

    uint256 amountOut = swap(intent.tokenIn, intent.tokenOut, intent.amountIn);

    require(amountOut >= intent.minAmountOut, "Slippage");

    transferCrossChain(intent.user, intent.tokenOut, amountOut, intent.dstChainId);

    }

    Recurring Patterns

    Across all five hacks, common themes emerge:

  • Signature replay — not including contract address or chain ID
  • Oracle manipulation — short TWAPs, centralized oracles
  • Economic security gaps — low cost to attack vs high cost to defend
  • Liquidity assumptions — assuming instant withdrawals, no slippage
  • Composability risks — cascading failures across protocols
  • Lessons Learned

    • Use EIP-712 for all signatures, with domain separators
    • Use 24h+ TWAPs or decentralized oracles (Chainlink, Pyth)
    • Require economic security from operators (skin in the game)
    • Add slippage protection to all swaps and intents
    • Test depeg scenarios and cascade failures

    Q1 2026 was brutal. Let's make Q2 safer.

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement