# 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:
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:
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:
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:
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:
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.