💰

    The Midnight Whale

    The Midnight Whale: Why Aave's USDC Rate Triples at 00:00 UTC Every Night

    ENGINEERING

    The Midnight Whale: Why Aave's USDC Rate Triples at 00:00 UTC Every Night

    8 min read
    pigi.finance team

    The short version

    • Our Aave V3 USDC vault page showed ~11% APR while Aave's own app showed ~3.2%
    • Both numbers were "correct" — they were just read at different times of day
    • A single wallet withdraws ~$191M of USDC from Aave ~40 minutes before midnight UTC and redeposits it ~10 minutes after, every night — tripling the displayed rate exactly while the clock strikes twelve
    • Our indexer sampled the rate at exactly 00:00 UTC, dead center of the spike — so every daily reading was inflated ~3.5×
    • Following the money changed our conclusion: the USDC is pooled with an unrelated $300M into one wallet across midnight, then everything goes back. That looks like a fund proving control of its assets at a reporting cut-off — the rate spike is a side effect, not the goal
    • We switched every Aave strategy to a time-weighted realized APR that a passing distortion cannot move, and recomputed 90 days of history

    A bug report that wasn't a bug

    Our vault page for Aave V3's USDC reserve on Ethereum mainnet showed a current APR of 11.10% and a 30-day average of 10.76%. Aave's own reserve page showed 3.24% for the same asset, same pool, same moment. TVL matched perfectly; only the rate was wrong. We tried to find the source of the error.

    We read the reserve's currentLiquidityRate directly from the chain at the exact block our indexer had sampled — the last block before 00:00 UTC — through three independent RPC providers. All three agreed: the rate really was 11.10% at midnight. And just 20 minutes later it really was 3.19%. Both numbers were true. The question changed from "where is our bug?" to "why does this rate spike at midnight?"

    11% at midnight

    11% apr stayed for 20 minutes only. This is not a market moving — it is a switch being flipped on and off:

    Time (UTC)USDC supply APR on-chain
    23:003.15%
    23:4511.07%
    23:55 – 00:0511.10% ← our indexer sampled here
    00:153.17%
    01:00 → 12:003.18 – 3.19%

    The elevated window lasts roughly 50 minutes and brackets 00:00 UTC almost symmetrically. Our pipeline took one sample per day — at exactly 00:00 UTC. For weeks, every sample landed inside that window. The result: a chart that honestly reported 10–12% every single day.

    The distortion has a distinctive shape. Here is our own APR chart for this pool, February through July, as it looked before the fix — a square wave. Those flat-topped plateaus are the sampler landing inside the spike on some nights and outside it on others; the blocks are an artifact of when we looked, not of what the pool paid:

    Aave USDC APR chart before the fix: a square wave alternating between a 3% baseline and 10-12% plateaus, with May 20 reading 9.94% APR and 8.02% 30-day moving average
    Before — instantaneous rate sampled at 00:00 UTC. May 20 reads 9.94% APR, 30-day MA 8.02%.

    And the same pool, same period, recomputed with the time-weighted method described below.

    Aave USDC APR chart after the fix: a smooth 3.5% line with one genuine late-April plateau preserved, May 20 reading 3.52% APR and 5.18% 30-day moving average
    After — time-weighted realized APR. The same May 20 now reads 3.52% APR, 30-day MA 5.18%.

    Same pool, same day, same on-chain history: 9.94% versus 3.52%. Only the question changed.

    The wallet that never sleeps through midnight

    Aave's supply rate is a function of utilization — borrowed funds divided by supplied funds. There are two ways to spike it: borrow a lot, or make the supply disappear. The Pool contract's event log answers which one happened here. No large borrows appear around midnight. Instead, one address — 0x56957e…0149 — performs the same choreography night after night: withdraw roughly $191 million of USDC about 40 minutes before midnight, redeposit it about 10 minutes after.

    Night (UTC)WithdrawRedeposit
    May 19 → 20$128,312,054 at 23:34:47 — 0xc176ab34…$128,340,415 at 00:32:47 — 0xbdc209ef…
    Jun 14 → 15$135,152,451 at 23:26:59 — 0x2b832301…$135,180,617 at 00:16:47 — 0x422b32fb…
    Jul 26 → 27$191,555,593 at 23:18:59 — 0xaed8f225…$191,583,064 at 00:11:59 — 0x6199efe9…
    Jul 27 → 28$191,598,990 at 23:19:11 — 0x3918bae7…$191,626,387 at 00:13:23 — 0x0c00b64b…
    Jul 28 → 29$191,642,333 at 23:21:23 — 0x1659a933…$191,669,682 at 00:10:23 — 0x060a1925…

    Same wallet, same window, for months — with the position growing from $128M in May to $191M in July. Pulling ~$190M out of a ~$2.15B pool pushes utilization past the interest model's kink, where the rate curve turns steep; the displayed supply APR jumps from ~3.2% to ~11.1%. Fifty minutes later the money returns and the curve relaxes. The forgone interest from sitting out of the pool for 50 minutes is on the order of a few hundred dollars per night — the manipulation, if that is what it is, costs almost nothing.

    It is still running. On the night of July 29 → 30 the wallet withdrew $191,685,848 at 23:21:11 (0x346b64a7…) and redeposited $191,713,320 at 00:08:47 (0x0689e5ba…).

    Follow the money and the motive changes

    Our first instinct was that someone was farming the rate itself — that the point was to make every dashboard sampling at midnight print a number three times too high. Tracing where the USDC actually goes says otherwise, and it is worth being precise about, because it turns a story about manipulation into a story about accounting.

    The withdrawn USDC does not sit idle for those fifty minutes. It moves to a second wallet, 0x31173e…bf18, where it meets an unrelated $300 million arriving from a third address about ninety seconds earlier. The two are combined and the whole sum is forwarded to a single address, 0xf1edbf…56a8. Midnight passes. Then the entire flow runs backwards and each pot returns to the yield strategy it came from. Here is the last cycle, on the genuine USDC contract:

    Time (UTC)AmountMovement
    23:21:11$191,685,848withdrawn from Aave
    23:21:59$300,027,472arrives at 0x31173e…bf18 from elsewhere
    23:23:35$191,685,848the Aave money joins it
    23:26:35$491,713,320combined, sent to 0xf1edbf…56a8
    — 00:00 UTC —
    00:03:11$491,713,320comes back, intact
    00:05:23$300,000,000returns to where it came from
    00:08:47$191,713,320redeposited into Aave

    The arithmetic is exact: $300,027,472 + $191,685,848 = $491,713,320, the sum that crosses midnight in one place. That detail is the whole argument. If the goal were to move Aave's interest rate, there would be no reason to consolidate the proceeds with $300 million of unrelated capital that was never in Aave. What the pattern does look like is an entity proving control of its assets in a single wallet at a daily cut-off — an audit or NAV snapshot taken at 00:00 UTC — and returning everything to work as soon as the photograph has been taken.

    On that reading the rate spike is not the objective at all. It is a side effect: an externality of a fund's reporting calendar landing on a public lending pool. That is a less dramatic story, and a more plausible one. The cost is real either way — Coin Metrics puts the extra interest paid by USDC borrowers during these windows at roughly $17,000 a night, on the order of $6 million a year — and the distortion to every daily-sampled rate feed is identical whatever the intent.

    A caution for anyone retracing this: the same wallets attract address-poisoning spam — tokens with names built from Unicode lookalikes of "USDC", sent from vanity addresses that mimic the real ones. They mirror the genuine amounts almost exactly. Every figure above is filtered to the real USDC contract; a naive scan of the wallet's transfers will show phantom movements that never happened.

    We were not the first to notice

    Having published a claim this specific, it is only fair to point at everyone who got there independently — in some cases well before us.

    • Reddit, May. A post asked why Aave's USDC yield spiked at the same time every day. The thread guessed at vault rebalancing, flash loans, Instadapp, Ethena — the pattern was visible to users months ago, but nobody named a wallet.
    • @0xG00gly on X, July 1. An on-chain researcher identified the exact address weeks before we did, noting it had been pulling USDC out of Aave just before midnight and returning it just after — "every single night" since January.
    • Coin Metrics, July 28. A full investigation, Why is Aave's USDC Utilization Rate Spiking? (State of the Network #374), reached the same wallet and the same fund-consolidation explanation, and measured what we could not: available USDC liquidity falling from roughly $210 million to as little as $33,000 inside the window, and the borrower cost quoted above. They also noted the schedule drifting — withdrawals sliding from ~23:20 to ~23:34, redeposits from ~00:34 to ~00:09.

    We found nothing in the Aave governance forum about this specific wallet. April's heavy discussion of USDC utilization concerned the liquidity crunch after the rsETH incident, not the nightly cycle.

    The fix

    The vulnerable design was sampling an instantaneous rate at a predictable time. Moving the sampling hour would work only until the schedule drifted — and it does drift. So we stopped asking "what is the rate right now?" and started asking "how much did suppliers actually earn over the last 24 hours?"

    Aave maintains a cumulative liquidity index per reserve: an on-chain accumulator that grows every second at the prevailing rate, and by which every aToken balance is multiplied. Its growth between two midnights is the yield a depositor realized. Every Aave APR we publish now comes from that growth, so the spike contributes only its true weight — fifty minutes out of 1,440:

    23.2 h × 3.19%  +  0.8 h × 11.1%  ≈  3.44% realized APR

    It is also structurally harder to distort: shifting a time-weighted average means holding the pool out of balance for hours, forgoing real interest on nine figures the whole time. No single instant defines the number any more. We rolled this out across every Aave strategy on 12 networks and recomputed 90 days of history; reserves nobody was disturbing barely moved, which is its own confirmation — Aave USDT shifted by two basis points. The USDC vault page now shows ~3.4%, the number a depositor actually gets.