DEV Community

ohmygod
ohmygod

Posted on

The Resolv USR Minting Exploit: How $100K in USDC Became $25M Through a Broken Swap Pipeline

On March 22, 2026, an attacker deposited 100,000 USDC into the Resolv protocol and walked away with approximately $25 million. The USR stablecoin — which had maintained a steady $1 peg through delta-neutral strategies on ETH and BTC — crashed to $0.025 on Curve Finance within minutes.

This wasn't a flash loan attack. It wasn't oracle manipulation in the traditional sense. It was something arguably worse: a fundamental disconnect between the request and finalization stages of a two-phase minting pipeline.

Let's break down what happened, why it happened, and what every stablecoin protocol should learn from it.

The Two-Phase Mint Architecture

Resolv's USR minting works through a two-step process:

  1. requestSwap — User deposits collateral (USDC) and requests a mint
  2. completeSwap — An off-chain validator or oracle confirms the request, and USR is minted

This is a common pattern in DeFi stablecoins. The two-phase approach is meant to add a validation layer: the protocol can verify collateral, check oracle prices, and ensure the mint amount is appropriate before finalizing.

The problem? The amount validation between these two phases was catastrophically broken.

The Attack Flow

Here's what the attacker did:

Step 1: Call requestSwap() with 100,000 USDC
Step 2: completeSwap() finalizes → 49,950,000 USR minted
Step 3: Repeat → additional 30,000,000 USR minted
Step 4: Convert USR → wstUSR → USDC/USDT → ETH
Step 5: Bridge out across multiple networks
Enter fullscreen mode Exit fullscreen mode

The 500x multiplier between deposit and mint is the smoking gun. The attacker deposited $100K and received 50 million USR — a ratio that should have been impossible under any valid oracle price.

Three Hypotheses (All Bad)

Security researchers at D2 Finance identified three potential root causes, each representing a different failure mode:

Hypothesis 1: Oracle Manipulation

The oracle feeding price data to completeSwap was deceived into reporting a wildly incorrect USDC/USR rate. If the oracle believed 1 USDC = 500 USR, the math would check out from the contract's perspective.

Why this matters: Oracles in two-phase systems are particularly dangerous because the price check happens asynchronously. The attacker has time to manipulate the oracle between request and completion.

Hypothesis 2: Off-Chain Signer Compromise

If Resolv uses an off-chain signer to authorize the completeSwap, and that signer's private key was leaked, the attacker could have signed arbitrary mint amounts. This would bypass all on-chain validation entirely.

Why this matters: Off-chain signers are a single point of failure. If the private key is compromised, the attacker has effectively unlimited minting authority.

Hypothesis 3: Missing Amount Validation

The simplest and most damning possibility — there was simply no check ensuring that the amount in completeSwap matched or was proportional to the amount in requestSwap. The request said "100K USDC" but the completion said "50M USR," and the contract didn't blink.

Why this matters: This is a basic invariant violation. The two-phase pattern is pointless if the finalization phase doesn't validate against the request phase.

The Exit Strategy

After minting 80 million unbacked USR, the attacker executed a textbook DeFi extraction:

  1. Wrapped USR → wstUSR to access additional liquidity venues
  2. Sold across every available DEX — Curve, Uniswap, and others
  3. Accepted massive slippage — when you're selling unbacked tokens, any return is pure profit
  4. Bridged to other networks — fragmenting the trail across chains

The aggressive selling caused USR to crash from $1.00 to $0.025 on Curve — a 97.5% depeg. Regular USR holders who had nothing to do with the exploit were caught in the crossfire.

The Damage

Metric Value
Attacker's investment ~$100,000 USDC
Unbacked USR minted 80,000,000
Estimated extraction ~$25,000,000
USR price low $0.025 (Curve)
Return on attack ~250x

Lessons for Stablecoin Protocols

1. Two-Phase Mints Need On-Chain Invariants

If your protocol uses a request-then-complete pattern, the completion must validate against the original request on-chain. Store the request parameters (amount, sender, timestamp) in contract storage and verify them during completion.

// BAD: completeSwap trusts external input for amount
function completeSwap(uint256 requestId, uint256 mintAmount) external {
    _mint(requests[requestId].user, mintAmount);
}

// GOOD: completeSwap derives amount from stored request
function completeSwap(uint256 requestId) external {
    SwapRequest memory req = requests[requestId];
    uint256 mintAmount = calculateMint(req.collateralAmount, getOraclePrice());
    require(mintAmount <= req.collateralAmount * MAX_RATIO, "ratio exceeded");
    _mint(req.user, mintAmount);
}
Enter fullscreen mode Exit fullscreen mode

2. Implement Mint Rate Limits

No single transaction should be able to mint more than a configurable percentage of total supply. If your stablecoin has $50M in TVL, a single mint of $50M should be impossible regardless of collateral.

uint256 public constant MAX_MINT_PER_TX = 1_000_000e18; // 1M USR max
uint256 public constant MAX_MINT_PER_HOUR = 5_000_000e18;

modifier mintLimited(uint256 amount) {
    require(amount <= MAX_MINT_PER_TX, "exceeds tx limit");
    hourlyMinted[currentHour()] += amount;
    require(hourlyMinted[currentHour()] <= MAX_MINT_PER_HOUR, "hourly limit");
    _;
}
Enter fullscreen mode Exit fullscreen mode

3. Oracle Sanity Bounds

Even if your oracle is correct 99.99% of the time, implement hard bounds on acceptable price ratios. A USDC/USR rate should never deviate more than a few percent from 1:1.

uint256 public constant MIN_RATE = 0.95e18; // 1 USDC = 0.95-1.05 USR
uint256 public constant MAX_RATE = 1.05e18;

function validateRate(uint256 rate) internal pure {
    require(rate >= MIN_RATE && rate <= MAX_RATE, "rate out of bounds");
}
Enter fullscreen mode Exit fullscreen mode

4. Time-Lock Large Mints

For mints above a threshold, introduce a mandatory delay. This gives the team (and monitoring bots) time to detect anomalies before funds are released.

5. Circuit Breakers Are Not Optional

If USR's price deviates by more than 5% from peg, all minting and redemption should pause automatically. The protocol should not continue operating normally while under attack.

The Bigger Picture

The Resolv exploit follows a pattern we've seen repeatedly in 2026:

  • Venus Protocol (March 15): Price manipulation on low-liquidity tokens → $3.7M bad debt
  • Aave CAPO misconfiguration (March 10): Oracle desync → $26M in erroneous liquidations
  • Step Finance (January 31): Key management failure → $27M treasury drain

The common thread? Insufficient validation at trust boundaries. Whether it's an oracle feed, an off-chain signer, or a two-phase swap, every point where external data enters your protocol is an attack surface.

Stablecoin minting is the highest-stakes trust boundary in DeFi. If you're building a protocol that creates money, your validation logic needs to be paranoid, redundant, and tested against adversarial scenarios — not just happy paths.

Audit Checklist for Two-Phase Mint Systems

  • [ ] Request parameters stored immutably on-chain
  • [ ] Completion validates against stored request (not external input)
  • [ ] Oracle prices bounded within reasonable ranges
  • [ ] Per-transaction mint limits enforced
  • [ ] Per-hour/per-day aggregate mint limits enforced
  • [ ] Time-lock on mints above threshold
  • [ ] Circuit breaker on price deviation from peg
  • [ ] Off-chain signer keys rotated regularly with multi-sig
  • [ ] Monitoring alerts on unusual mint volumes
  • [ ] Emergency pause function with multi-sig or timelock

The Resolv exploit was preventable with any one of these controls. The $25 million question is: does your protocol have them?


Follow for more DeFi security analysis. Previous coverage: Stablecoin Mint Path Auditing: A 12-Point Security Checklist After the $25M USR Exploit


Tags: security, blockchain, defi, solidity

Top comments (0)