All audits
FailedEthereumIndependent

UwU Lend Oracle (2024)

Smart contract security audit by TierZero · 2024-06-15

Independent research. This is an unsolicited security analysis published by TierZero — not a commissioned audit, and no certificate is issued. It reflects our own review of public code and on-chain events.

Findings summary

1
Critical
2
High
2
Medium
1
Low
1
Informational

Independent research — not a commissioned audit. This report analyzes a publicly documented incident using information from post-mortems, on-chain data, and community writeups. TierZero was not involved with UwU Lend and had no access to non-public information.

What the protocol did

UwU Lend was an Ethereum money market built on a Compound/Aave-style isolated-pool lending model, positioning itself as a home for yield-bearing and "exotic" collateral that mainstream markets wouldn't list — locked CVX, various Curve LP receipts, and Ethena's USDe / sUSDe among them. Like any fork of this design, its solvency depends entirely on one thing: the price oracle has to reflect an asset's real market value at the moment collateral is valued and debt is issued. In June 2024, that assumption broke, and the protocol lost on the order of $19-20 million in an initial attack, followed by a second, smaller drain of roughly $3-4 million hours later as the attacker moved to extract remaining value — a combined loss north of $23 million.

The vulnerability

The proximate cause was a price oracle for sUSDe (and related USDe-denominated collateral) that derived its value from a Curve pool's on-chain exchange rate rather than from a manipulation-resistant, time-weighted source. Reading a spot AMM price as if it were a reliable oracle is one of the oldest bug classes in DeFi, and it recurs because the failure mode is invisible in normal conditions — the price is "correct" until someone has enough capital to move it for one block.

The flawed pattern looks something like this:

// Flawed: spot exchange rate read directly from a Curve pool
function getUnderlyingPrice(address cToken) external view returns (uint256) {
    ICurvePool pool = ICurvePool(sUSDePoolAddress);
    // get_dy / exchange rate reflects CURRENT pool balances —
    // trivially shiftable with a large, atomic swap
    uint256 rate = pool.get_dy(0, 1, 1e18);
    return rate * BASE_PRICE / 1e18;
}

Nothing here checks pool depth, compares against a secondary source, or bounds how far the reported rate can move between reads. A pool that is fine for swaps of normal retail size is not automatically fine as a price feed once someone can bring flash-loan-sized capital against it.

How the exploit worked

  1. The attacker sourced a large flash loan and used part of it to acquire a substantial sUSDe/USDe position and open borrowing positions across UwU Lend's markets.
  2. Using the borrowed capital, the attacker executed large, deliberately lopsided trades through the Curve pool UwU Lend's oracle depended on, pushing the pool's reported exchange rate for sUSDe well above its fair value.
  3. Because the oracle read that pool's rate directly, UwU Lend's risk engine marked the attacker's sUSDe collateral as worth substantially more than it actually was.
  4. Against that inflated collateral value, the attacker borrowed out other assets held in UwU Lend's markets — draining liquidity the protocol had no way to reconcile once the manipulated price reverted.
  5. The attacker unwound the Curve position, repaid the flash loan, and walked away with the difference, leaving UwU Lend holding bad debt against collateral now correctly priced.
  6. A second attack followed shortly after, reportedly exploiting a related weakness to extract further funds before the team could fully pause the protocol — a reminder that a single oracle failure often exposes more than one path to drain a system once the underlying pricing assumption is shown to be wrong.

This is structurally the same failure as Mango Markets, Cream Finance's Iron Bank markets, and half a dozen other "collateral valued by a spot AMM price" incidents — only the specific pool and asset changed.

How an audit catches this

This is a mechanical, checklist-catchable class of bug, which is exactly why it keeps recurring in protocols that skip a dedicated oracle review. On every lending or money-market engagement we run:

  • Oracle source inventory. Enumerate every price feed in scope and classify each as decentralized (Chainlink/Pyth), TWAP/EMA-derived, or raw spot (AMM get_dy, balanceOf ratios, getReserves). Any raw-spot feed backing a borrowable market is flagged by default, no exceptions.
  • Flash-loan simulation. Fork mainnet at a recent block, size a flash loan against the actual liquidity of the feeder pool, and measure how far the reported price moves in one atomic transaction. If a single tx can move collateral valuation more than a few percent, that's a finding, not a theoretical concern.
  • Cross-block manipulation tests. Some oracles resist single-block manipulation but not persistence across two or three blocks — worth testing separately since it changes the attacker's cost model (capital-at-risk time vs. flash-loan atomicity).
  • Deviation and staleness bounds. Confirm there's a sanity check comparing the AMM-derived price against an independent reference, with a bounded max deviation and a circuit breaker or pause path when it's exceeded.
  • Isolation blast-radius review. Even with a good oracle, check whether one mispriced market can drain shared liquidity versus staying contained to its own pool.

This is the same discipline we apply during a smart contract audit on any protocol accepting AMM-derived or yield-bearing collateral, and it's worth pairing with a full code review audit pass on the oracle and liquidation modules specifically, since that's where this class of bug hides. Teams building the lending logic itself benefit from involving audit-minded engineers during smart contract development rather than retrofitting oracle safety after a design is locked in — the same reasoning we've laid out when discussing Solidity Permit2 approval risk on EVM chains, where a convenience mechanism quietly widens the attack surface.

Remediation

  • Replace raw spot reads with a TWAP or EMA of sufficient window length relative to the pool's realistic flash-loan exposure — long enough that manipulating it costs more than the value at risk.
  • Add a secondary, independent price source (Chainlink where available) and reject or clamp any AMM-derived price that deviates beyond a defined threshold.
  • Cap loan-to-value ratios more conservatively for collateral types whose only pricing source is a single liquidity pool, and size those caps to actual pool depth, not a fixed protocol-wide default.
  • Build a pause/circuit-breaker path that triggers automatically on anomalous price deltas, not one that depends on a human noticing in time — the gap between the first and second UwU Lend attack shows how costly that delay is. The same cross-protocol composability risk shows up when cross-program invocation patterns in DeFi let one contract's bad state propagate into another's, even outside the EVM.
  • Re-review the oracle configuration whenever a new collateral type is listed — the initial design being sound doesn't mean the fifth asset added later inherits the same guarantees.

Protocols listing yield-bearing or LP-derived collateral should treat oracle design as the highest-risk surface in the codebase, not an implementation detail — if that hasn't had a dedicated pass, a smart contract audit focused specifically on pricing and liquidation logic is the place to start.

Need your contracts audited?

Manual review + tooling across TON, Solana and EVM — certificate and full report included.

Get an audit