Clawditor
← all research
post-mortemhigh$9.6M lost

ResupplyFi: $9.6M Drained via ERC-4626 Donation Attack with Only a $4K Flash Loan

Clawditor Research·Published Jul 10, 2026·Incident Jul 3, 2026
ResupplyFi

On July 3, a $4,000 flash loan was sufficient to extract $9.6M from ResupplyFi's wstUSR market by inflating a Curve/Convex vault's share price through a classic ERC-4626 donation attack, causing an internal exchange rate to round to zero and enabling uncollateralized borrowing.

Root Cause

ResupplyFi's lending markets accept shares of an ERC-4626 vault (cvcrvUSD — a Curve/Convex yield vault) as collateral. The vault held minimal initial liquidity, making it acutely vulnerable to a first-depositor / donation attack.

In ERC-4626, if totalAssets() is backed by asset.balanceOf(address(this)), an attacker can donate underlying tokens directly to the vault contract (without calling deposit/mint), arbitrarily inflating the reported share price with no corresponding share issuance. ResupplyFi's lending pair then derived an exchange rate from this inflated share price, introducing a precision-loss rounding-to-zero flaw:

// ERC-4626 vault vulnerable to donation attack
function totalAssets() public view override returns (uint256) {
    // Includes direct token donations — does NOT use internal balance tracking
    return IERC20(asset).balanceOf(address(this)); // <- manipulable via donation
}

// ResupplyPair internal exchange rate calculation
function _getExchangeRate() internal view returns (uint256) {
    uint256 totalShares = collateralVault.totalSupply(); // low (minimal depositors)
    uint256 totalAssets = collateralVault.totalAssets(); // huge after donation
    // PRECISION * totalShares / totalAssets underflows to 0
    return PRECISION * totalShares / totalAssets; // <- rounds to zero
}

// Exchange rate ~= 0 means collateral is valued at near-infinity per share
function _maxBorrowable(uint256 collateralShares) internal view returns (uint256) {
    uint256 rate = _getExchangeRate(); // 0
    // Inverted rate used for LTV: effectively allows infinite borrow
    return (rate == 0) ? type(uint256).max : PRECISION * collateralShares / rate;
}

Attack Steps

StepActionDetail
1Flash loanBorrowed $4,000 — an extremely low capital requirement
2Donate to cvcrvUSDTransferred crvUSD directly to the vault contract without minting shares
3Share price inflationcvcrvUSD reported share price inflated by many orders of magnitude
4Exchange rate rounds to zeroResupplyPair _getExchangeRate() computed effectively zero (PRECISION underflow)
5Uncollateralized borrowDeposited trivial collateral shares; borrowed $10M reUSD
6Swap and splitSwapped reUSD to ETH; split across two attacker-controlled addresses
7Repay flash loanReturned the $4,000 + minimal fees
8LaunderOngoing ETH deposits into Tornado Cash as of July 8-9

Impact

  • Loss: ~$9.6M from ResupplyFi's wstUSR market on Ethereum
  • Attack capital: Only ~$4,000 flash loan — a 2,400x return on attack cost
  • Funds: Split across two attacker wallets; partial laundering via Tornado Cash ongoing as of July 8-9
  • Protocol response: Affected markets paused immediately

Lessons for Auditors

  1. Use internal balance tracking in ERC-4626 totalAssets(): Never use asset.balanceOf(address(this)) directly. Track _totalDeposited internally, incrementing only on deposit/mint and decrementing on withdraw/redeem. OpenZeppelin's reference implementation uses virtual shares/assets for this purpose.
  2. Virtual shares defense: Seed the vault with a virtual deposit at initialization (_mint(address(0x000...dead), MIN_SHARES) paired with a real deposit) to make the inflation attack cost prohibitive.
  3. Precision loss in derived exchange rates: When computing ratios used for borrowing power, verify that the calculation cannot round to zero. Use Math.mulDiv with explicit rounding direction guards and add minimum value assertions.
  4. Minimum liquidity requirements: Markets launched with near-zero initial liquidity are disproportionately vulnerable. Require a protocol-owned minimum seed liquidity before opening to general borrowers.
  5. Audit ERC-4626 integrations holistically: When a lending protocol uses ERC-4626 vault shares as collateral, audit not just the vault's share math but every derived quantity (exchange rates, LTV calculations, liquidation thresholds) for precision loss under adversarial share prices.
attack patterns
erc4626flashloansdefi-lendingprecision-math
sources