Skip to main content

On This Page

Donation Attacks on Compound-Fork Lending Protocols: Dissecting the Venus Protocol THE Exploit

2 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Donation Attacks on Compound-Fork Lending Protocols: Dissecting the Venus Protocol THE Exploit

Venus Protocol on BNB Chain was drained of approximately $3.7 million on March 15, 2026. The attacker spent nine months accumulating 84% of the THE token supply cap before bypassing deposit mechanisms to inflate the vToken exchange rate.

Why This Matters

The persistence of donation attacks highlights a critical gap between the legacy architecture of Compound V2 forks and modern security requirements. While Compound V2 was revolutionary in 2020, its reliance on raw balanceOf() calls for accounting creates a vulnerability where direct ERC-20 transfers bypass high-level logic like supply caps and minting restrictions, leading to over $230 million in cumulative historical losses across the ecosystem.

Key Insights

  • Attacker accumulated 14.5 million THE tokens over nine months (June 2025 - March 2026) to avoid triggering price alerts.
  • Direct token transfers to the vTHE contract inflated the exchange rate from approximately 1.03 to 3.17 without increasing the cToken supply.
  • Historical losses from this vulnerability class include Hundred Finance ($7.4M in 2022) and Euler Finance ($197M in 2023).
  • Compound V3 (Comet) and Aave V3 mitigate this risk by using internal scaled balance accounting rather than raw token balances.
  • The attack was amplified by an oracle feedback loop where borrowed funds were used to manipulate thin-market spot prices.

Working Examples

Vulnerable Compound V2 exchange rate logic using raw balanceOf() which includes ‘donated’ tokens.

function getCashPrior() internal view returns (uint) {
  return IERC20(underlying).balanceOf(address(this));
}

function exchangeRateStored() public view returns (uint) {
  uint _totalSupply = totalSupply;
  if (_totalSupply == 0) return initialExchangeRateMantissa;
  uint totalCash = getCashPrior();
  uint exchangeRate = (totalCash + totalBorrows - totalReserves) * 1e18 / _totalSupply;
  return exchangeRate;
}

Defense via internal accounting: tracking deposits and withdrawals manually to ignore direct transfers.

uint256 internal _totalCash;

function getCashPrior() internal view returns (uint) {
  return _totalCash;
}

function mintInternal(uint mintAmount) internal {
  _totalCash += mintAmount;
}

function redeemInternal(uint redeemAmount) internal {
  _totalCash -= redeemAmount;
}

Practical Applications

  • Protocol teams should implement donation detection by routing unexpected balance increases to ‘totalReserves’ instead of the exchange rate numerator.
  • Risk engines should dynamically scale collateral factors based on on-chain liquidity to prevent thin-market manipulation.
  • Auditors must verify that supply caps are enforced at the balance level rather than just within the mint() function entry point.

References:

Continue reading

Next article

Build Your First MCP Server in 10 Minutes with TypeScript

Related Content