# Resupply (June 2025, Ethereum)

## 1. What happened

- **Date:** June 26, 2025; the exploit tx ran at 01:53:59 UTC, ~1h35m after the target market was deployed at 00:18:47 UTC that day (B = https://blocksec.com/blog/in-depth-analysis-and-reflections-on-the-resupply-protocol-attack-incident; Q = https://quillaudits.com/blog/hack-analysis/resupply-hack-analysis; K = https://ackee.xyz/blog/resupply-hack-analysis/). rekt.news's body says "June 25th", but its own dateline is Friday June 27, 2025 and every primary source says June 26, so June 26 is used (R = https://rekt.news/resupplyfi-rekt).
- **Chain:** Ethereum mainnet (B).
- **Amount lost (sources disagree):** ~$10M (B); "$10M of reUSD bad debt" (P = Resupply post-mortem, https://web.archive.org/web/20250706/https://mirror.xyz/0x521CB9b35514E9c8a8a929C890bf1489F63B2C84/ygJ1kh6satW9l_NDBM47V87CfaQbn2q0tWy_rtp76OI); $9.8M (R); $9.6M (D = https://decrypt.co/327148/hacker-drained-9-6-million-from-defi-stablecoin-protocol-resupply); $9.56M (K); $9.5M (Q; https://cryptopotato.com/stablecoin-protocol-resupply-suffers-9-5m-exploit/; https://www.theblock.co/news/ecosystems/2025-06-26-resupply-exploited-blocksec-359764). Range ~$9.5M-$10M.
- **What was taken:** the attacker borrowed 10,000,000 reUSD from the freshly deployed crvUSD/wstUSR "ResupplyPair" market (0x6e90) with ~1 wei of vault-share collateral, swapped to ~9.33M scrvUSD, then ~9.81M crvUSD, then WETH, extracting ~1,581 ETH (B, Q, https://www.theblock.co/news/ecosystems/2025-06-26-resupply-exploited-blocksec-359764).

## 2. Root cause

This was **not** a key, signer or multisig compromise; it was a flaw in the price/configuration logic. The market priced its ERC-4626 collateral (CurveLend cvcrvUSD shares) as `_price = IERC4626(vault).convertToAssets(1e18)` and computed the borrow/collateral exchange rate with floor division, `_exchangeRate = 1e36 / oracle.getPrices(collateral)` (B, K). The market was deployed while its CurveLend vault held zero deposits, so the attacker inflated the share price by donating 2,000 crvUSD straight to the vault controller (0x8970), then depositing ~2 crvUSD to mint 1 wei of share, making 1 wei of shares worth ~2e18 crvUSD, a price near 2e36 (B, P). Above 1e36 the integer division rounded **down to zero**: `_exchangeRate = 0`. The borrow solvency check computed `_ltv = (_borrowerAmount * _exchangeRate * LTV_PRECISION) / EXCHANGE_PRECISION / _collateralAmount`, so a zero rate forced `_ltv = 0` and `_ltv <= _maxLTV` passed for any loan size, letting the attacker borrow the pair's full 10M reUSD debt limit (P; B; K). BlockSec notes Resupply used a price-oracle implementation similar to a Curve template and that Curve's own docs had already flagged its applicability limits, which Resupply appears not to have heeded (B). Resupply's post-mortem says the vulnerable code was in scope for multiple audits and independent reviews, none of which reported it, and the issue was unique to this pair (P).

## 3. Pattern

**Class:** an ERC-4626 vault inflation / empty-market "donation" attack combined with a rounding-to-zero precision bug that degenerates a solvency check. Not reentrancy, not missing access control, not a stolen key.

**Vulnerable shape:** (1) a price path divides by a value the attacker can inflate, with no lower bound; (2) a fresh market is initialized with no/negligible liquidity so its share price is trivially manipulable; (3) a safety check multiplies by that manipulated price, so driving it to zero makes the check pass unconditionally. The flaw:

```
price        = vault.convertToAssets(1e18)   // attacker donation -> ~2e36
exchangeRate = 1e36 / price                  // floor division -> 0
ltv          = borrowerAmount * exchangeRate * P / E / collateralAmount  // 0
require(ltv <= maxLTV)                       // 0 <= maxLTV -> always true
```

## 4. The check that would have caught it

- **Fuzz/invariant test on the solvency math.** Fuzz the oracle price across the full `uint256` range (including >= 1e36) and assert the invariant **`_exchangeRate > 0`**, and that a 1-wei-collateral position is never `_isSolvent` while debt > 0. Enforce the same invariant in code with `require(exchangeRate > 0)` (or round the division up) so a manipulated price cannot zero the rate (K; P).
- **Deployment/initialization validation.** Before enabling a market, require the ERC-4626 vault to be seeded with a minimum non-zero liquidity (or reject a market whose `convertToAssets(1e18)` exceeds a sane bound). ChainSecurity said the vault code was in scope but "ensuring that they are securely initialized wasn't in scope"; Electi confirmed the price-rounding issue was in scope but the deployment was not, so the gap sat between code review and deployment validation (R).
- **Review rule.** Any `a / b` whose `b` derives from an external or attacker-influenceable oracle must carry a documented lower bound or minimum-price guard before it can feed a health/solvency calculation (K).

## 5. Sources

- https://blocksec.com/blog/in-depth-analysis-and-reflections-on-the-resupply-protocol-attack-incident
- https://web.archive.org/web/20250706/https://mirror.xyz/0x521CB9b35514E9c8a8a929C890bf1489F63B2C84/ygJ1kh6satW9l_NDBM47V87CfaQbn2q0tWy_rtp76OI (Resupply's post-mortem; mirror.xyz original)
- https://rekt.news/resupplyfi-rekt
- https://ackee.xyz/blog/resupply-hack-analysis/
- https://quillaudits.com/blog/hack-analysis/resupply-hack-analysis
- https://decrypt.co/327148/hacker-drained-9-6-million-from-defi-stablecoin-protocol-resupply
- https://cryptopotato.com/stablecoin-protocol-resupply-suffers-9-5m-exploit/
- https://www.theblock.co/news/ecosystems/2025-06-26-resupply-exploited-blocksec-359764
- https://paragraph.com/@resupply/BQGdyz30aJ1uZJ2Lc2UT
